"use strict";
cc._RF.push(module, 'ae7d7j8qCJHEr/tVZKmu8hm', 'GamePageController');
// scripts/game/GamePageController.ts

"use strict";
// Learn TypeScript:
//  - https://docs.cocos.com/creator/2.4/manual/en/scripting/typescript.html
// Learn Attribute:
//  - https://docs.cocos.com/creator/2.4/manual/en/scripting/reference/attributes.html
// Learn life-cycle callbacks:
//  - https://docs.cocos.com/creator/2.4/manual/en/scripting/life-cycle-callbacks.html
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
var GameBean_1 = require("../bean/GameBean");
var BlockAnimationManager_1 = require("./BlockAnimationManager");
var SimpleGameEventHandler_1 = require("./SimpleGameEventHandler");
var GameMgr_1 = require("../common/GameMgr");
var GamePlayerController_1 = require("../pfb/GamePlayerController");
var GlobalBean_1 = require("../bean/GlobalBean");
var LeaveDialogController_1 = require("../hall/LeaveDialogController");
var AudioManager_1 = require("../util/AudioManager");
var GlobalManagerController_1 = require("../GlobalManagerController");
var Config_1 = require("../util/Config");
var Tools_1 = require("../util/Tools");
var CongratsDialogController_1 = require("./CongratsDialogController");
var GameLayoutController_1 = require("./GameLayoutController");
var VirtualJoystickController_1 = require("./VirtualJoystickController");
var WebSocketManager_1 = require("../net/WebSocketManager");
var MessageId_1 = require("../net/MessageId");
var MenuLayoutController_1 = require("../MenuLayoutController");
var InfoDialogController_1 = require("../hall/InfoDialogController");
var _a = cc._decorator, ccclass = _a.ccclass, property = _a.property;
var GamePageController = /** @class */ (function (_super) {
    __extends(GamePageController, _super);
    function GamePageController() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.boardBtnBack = null; //返回按钮
        // 新增：强制结束游戏按钮（在编辑器中拖拽绑定该节点以启用）
        _this.forceCloseBtn = null;
        _this.leaveDialogController = null; // 退出游戏弹窗
        _this.congratsDialogController = null; //结算弹窗
        _this.gameLayoutController = null; // 游戏布局控制器
        _this.menuLayoutController = null; //菜单按钮
        _this.infoDialogController = null; // 游戏介绍弹窗
        // ========== 坐标显示测试功能 ==========
        _this.coordinateLabel = null; // 坐标显示标签
        // ========== 摇杆控制相关 ==========
        _this.joystickController = null; // 摇杆控制器
        _this.playerNode = null; // 玩家节点
        // 统一动物预制体（包含骨骼动画）
        _this.animalPrefab = null; // 统一动物预制体，通过骨骼动画切换不同动物
        // ========== 倒计时系统相关 ==========
        _this.countdownNode = null; // 倒计时节点（包含骨骼动画组件）
        // ========== 回合数显示相关 ==========
        _this.roundDisplayNode = null; // 回合数显示节点
        // ========== 难度升级展示相关 ==========
        _this.jingbaoNode = null; // 警报节点（难度升级动画）
        // ========== Game Over 显示相关 ==========
        _this.gameoverNode = null; // 自己死亡后显示的GameOver节点
        _this.deathEffectPrefab = null; // 死亡特效预制体
        // ========== 调试信息显示 ==========
        _this.debugInfoLabel = null; // 调试信息标签（中文参数）
        // ========== 网络状态显示节点 ==========
        _this.net = null; // 顶部网络信号图标
        _this.Network = null; // 网络状态横幅/额外提示
        // ========== 玩家头像预制体相关 ==========
        _this.playerAvatarControllers = []; // 玩家头像预制体控制器数组
        // 方块相关
        _this.blockParent = null; // 方块父节点（包含现有方块）
        _this.particle = null;
        _this.particle1 = null;
        _this.playerMoveSpeed = 400;
        _this.infoClick = null; // 游戏介绍弹窗点击事件
        _this.moveSpeedScale = 0.3;
        _this.isLeaveGameDialogShow = false; //是否显示退出游戏的弹窗
        _this.isCongratsDialog = false; //是否显示结算的弹窗
        // 摇杆控制相关状态
        _this.currentPlayerVelocity = cc.Vec2.ZERO;
        _this.isPlayerMoving = false;
        // 玩家动物相关
        _this.myPlayerData = null; // 自己的玩家数据
        _this.myAnimalNode = null; // 自己的动物节点
        // 所有玩家动物管理
        _this.allPlayerAnimals = new Map(); // 所有玩家的动物节点 <userId, animalNode>
        _this.allPlayerData = new Map(); // 所有玩家的数据 <userId, PlayerData>
        // ========== 帧同步相关变量 ==========
        _this.clientFrameId = 0; // 客户端帧ID计数器
        _this.frameInputBuffer = []; // 当前批次的输入缓冲区
        _this.lastBatchSendTime = 0; // 上次发送批量数据的时间
        _this.batchSendInterval = 50; // 50ms发送间隔
        // ========== 精度控制常量 ==========
        _this.PRECISION_DECIMALS = 6; // 游戏精度：小数点后6位
        // 网络延迟补偿和时间同步
        _this.serverTimeOffset = 0; // 服务器时间偏移
        _this.networkLatency = 0; // 网络延迟（毫秒）
        _this.lastPingTime = 0; // 上次ping时间
        // 预测-回滚-确认相关
        _this.frameHistory = new Map(); // 帧历史记录
        _this.confirmedFrameId = 0; // 最新确认的帧ID
        _this.maxHistoryFrames = 60; // 最多保存3秒的历史（60帧）
        _this.predictionBuffer = 3; // 预测缓冲帧数
        // 朝向控制相关（度/秒），从原来以60FPS为基准的数值改为真实“度/秒”语义
        _this.rotationSmoothSpeed = 400.0;
        _this.lastMovementDirection = cc.Vec2.ZERO; // 上次移动方向
        // 锁步帧同步系统 - 调整为100FPS以确保50ms内产生5帧
        _this.logicFrameRate = 100; // 逻辑帧率：每秒100帧
        _this.fixedDeltaTime = 0.01; // 固定时间间隔：10ms (100FPS)，50ms内产生5帧
        _this.logicFrameTimer = 0; // 逻辑帧计时器
        _this.currentLogicFrame = 0; // 当前逻辑帧
        // 简化的等待机制（保留基本的帧清理）
        _this.maxWaitFrames = 3; // 最大等待帧数
        // 动物动画管理
        _this.animalAnimations = new Map(); // 每个玩家的骨骼动画组件
        _this.lastMovementTimes = new Map(); // 每个玩家最后移动时间
        _this.currentAnimationStates = new Map(); // 当前动画状态
        _this.IDLE_TIMEOUT = 3000; // 3秒无移动触发待机动画
        // 提示动画管理
        _this.tishiAnimations = new Map(); // 每个玩家的提示骨骼动画组件
        _this.tishiNodes = new Map(); // 每个玩家的提示节点
        // 动物类型映射
        _this.ANIMAL_TYPES = {
            'red': 'huli',
            'yellow': 'daishu',
            'green': 'yingwu',
            'pink': 'zhu'
        };
        // 动画状态类型
        _this.ANIMATION_STATES = {
            STATIC: 'jingzhi',
            IDLE: 'daiji',
            WALK: 'zoulu' // 行走
        };
        // 渲染插值系统 - 让20FPS逻辑帧在60/120FPS渲染下变得丝滑
        _this.logicStates = new Map(); // 每个玩家的逻辑状态历史
        _this.renderStates = new Map(); // 每个玩家的当前渲染状态
        _this.maxLogicStateHistory = 8; // 减少历史状态数量，优化内存使用
        // 对象池优化 - 减少GC压力
        _this.vec2Pool = []; // Vec2对象池
        _this.poolSize = 50; // 对象池大小
        _this.poolIndex = 0; // 当前池索引
        // 平滑移动系统 - 解决直接设置位置导致的卡顿问题
        _this.targetPositions = new Map(); // 每个玩家的目标位置
        _this.playerMovingStates = new Map(); // 每个玩家是否正在移动到目标
        _this.moveSpeed = 400; // 像素/秒，控制追赶服务器位置的速度
        _this.positionThreshold = 4; // 距离阈值，小于此值认为已到达目标（速度400时放宽，避免抖动）
        // 速度400的情况下，限制和解阶段的最大追赶速度，避免瞬间吸附
        _this.maxReconciliationSpeed = 480; // 1.2x 本地速度
        // 回滚误差日志节流
        _this.lastErrorLogTime = 0;
        // 远端玩家显示策略：true 表示优先使用“渲染插值”，禁用基于 target 的追赶，以避免两套系统争抢写位置
        _this.useRenderInterpolationForRemotes = true;
        // 渲染延迟/抖动缓冲系统 - 优化版
        _this.renderDelay = 80; // 减少渲染延迟，提升响应性
        _this.jitterBufferSize = 150; // 减少缓冲区大小
        _this.adaptiveRenderDelay = 80; // 自适应渲染延迟
        _this.networkJitterHistory = []; // 网络抖动历史记录
        _this.maxJitterHistorySize = 10; // 减少历史记录数量
        _this.lastFrameReceiveTime = 0; // 上次接收帧的时间
        _this.frameReceiveIntervals = []; // 帧接收间隔历史
        // 抖动缓冲优化
        _this.jitterSum = 0; // 抖动总和（用于快速计算平均值）
        _this.jitterCount = 0; // 抖动计数
        // 网络指示状态缓存，避免频繁重复加载资源
        _this._netStatus = 'good';
        _this.NET_ICON_PATH = {
            good: 'net_res/icon_signal_green',
            medium: 'net_res/icon_signal_yellow',
            poor: 'net_res/icon_signal_red'
        };
        _this.NETWORK_BANNER_PATH = {
            medium: 'net_res/Network_bad',
            poor: 'net_res/Network_poor'
        };
        // 坐标显示测试功能
        _this.isCoordinateDisplayEnabled = true; // 是否启用坐标显示
        // FPS统计
        _this.fpsAccumTime = 0;
        _this.fpsFrameCount = 0;
        _this.currentFps = 0;
        // 方块动画管理器
        _this.blockAnimationManager = null;
        // 游戏事件处理器
        _this.gameEventHandler = null;
        // ========== 玩家掉落检测相关 ==========
        // 棋盘配置 - 大地图边界坐标
        _this.BOARD_CONFIG = {
            // 大地图像素坐标范围（边界坐标）
            LEFT_X: -360,
            RIGHT_X: 360,
            BOTTOM_Y: -344,
            TOP_Y: 376,
            // 格子配置
            GRID_SIZE: 80,
            GRID_COUNT: 9,
            // 玩家配置
            PLAYER_SIZE: 60,
            SURVIVAL_RATIO: 0.4 // 90%体积判定
        };
        // 掉落检测状态
        _this.fallenPlayers = new Set(); // 已掉落的玩家ID
        _this.fallingPlayers = new Set(); // 正在掉落中的玩家ID（位置锁定）
        _this.currentGamePhase = 1; // 当前游戏阶段
        _this.fallenBlocks = new Set(); // 已掉落的方块坐标集合 "x,y"
        _this.isInputBlocked = false; // 是否禁止输入
        _this.isFrameInputStopped = false; // 是否停止发送FrameInput（eventType: 7时触发）
        _this.isReconnecting = false; // 是否正在经历断线重连流程（收到权威帧后恢复发送）
        // ========== 倒计时系统状态 ==========
        _this.isCountdownActive = false; // 是否在倒计时阶段
        _this.lastCountdownEventId = ""; // 上次倒计时事件ID，防止重复处理
        _this.countdownTimer = 0; // 倒计时定时器ID
        _this.isWaitingPhase = false; // 是否在等待阶段（GameEventWaiting到GameEventRoundWaiting）
        // 硬冻结本地玩家（1~2期间绝对不动）
        _this.isLocalFrozen = false;
        _this.frozenLocalPos = cc.Vec2.ZERO;
        // ========== 回合数显示状态    // 回合数显示相关
        _this.currentRound = 1; // 当前回合数
        // 死亡特效相关
        _this.deathEffectNodes = new Map(); // 玩家死亡特效节点映射
        // 缓存精度计算常量以提升性能
        _this.PRECISION_FACTOR = Math.pow(10, _this.PRECISION_DECIMALS);
        // 碰撞检测策略配置
        _this.collisionStrategy = 'hybrid';
        _this.lastServerPosition = null;
        _this.consecutiveCollisionFrames = 0;
        // 碰撞跟随后端模式：被其他玩家顶住时，前端不再做本地预测，严格跟随后端位置
        _this.isCollisionFollowing = false;
        _this.collisionReleaseFrames = 0; // 连续无碰撞帧计数，用于退出碰撞跟随模式
        // 和解频率监控 - 优化版
        _this.reconciliationHistory = [];
        _this.MAX_RECONCILIATION_HISTORY = 10;
        _this.FREQUENT_RECONCILIATION_THRESHOLD = 6; // 降低阈值：10秒内超过6次和解认为频繁
        _this.consecutiveOverThresholdFrames = 0; // 连续超过和解阈值的帧计数（仅本地玩家使用）
        // 预测缓冲优化
        _this.localPredictionBuffer = cc.v2(0, 0); // 本地预测缓冲位置
        _this.predictionConfidence = 1.0; // 预测置信度
        _this.networkDelayCompensation = 0; // 网络延迟补偿
        // 和解优化参数 - 更保守的设置
        _this.reconciliationSmoothness = 0.04; // 更平滑的和解系数（速度400下减小瞬时拉扯）
        _this.adaptiveThreshold = 20; // 调整为20，降低触发和解的距离阈值
        _this.lastReconciliationTime = 0; // 上次和解时间
        _this.maxReconciliationThreshold = 90; // 最大阈值限制（允许根据网络状况上调）
        // 弱网优化系统
        _this.networkQuality = 'good'; // 网络质量评估
        _this.packetLossRate = 0; // 丢包率
        _this.averageLatency = 0; // 平均延迟
        _this.latencyHistory = []; // 延迟历史
        _this.lastPacketTime = 0; // 上次收包时间
        _this.missedPackets = 0; // 丢失包数
        _this.totalPackets = 0; // 总包数
        _this.packetWindow = []; // 固定窗口记录最近N个包是否丢失（1丢 0未丢）
        _this.packetWindowSize = 100; // 丢包统计窗口大小
        // 弱网下的特殊策略
        _this.weakNetworkMode = false; // 是否启用弱网模式
        _this.extrapolationEnabled = false; // 是否启用外推预测
        _this.positionBufferSize = 5; // 位置缓冲区大小
        _this.playerPositionBuffers = new Map(); // 玩家位置缓冲
        // 和解状态管理
        _this.reconciliationStates = new Map();
        /**
         * 渲染插值更新 - 核心丝滑功能 + 抖动缓冲
         * 在20FPS逻辑帧之间进行平滑插值，让60/120FPS渲染变得丝滑
         * 使用渲染延迟来缓冲网络抖动，确保渲染的稳定性
         */
        // 渲染插值缓存（性能优化）- 增强版
        _this.lastRenderTime = 0;
        _this.MIN_RENDER_INTERVAL = 8; // 最小渲染间隔(毫秒)，约120FPS
        // 帧率自适应机制
        _this.frameTimeHistory = []; // 帧时间历史
        _this.maxFrameTimeHistory = 30; // 最多记录30帧
        _this.adaptiveRenderInterval = 8; // 自适应渲染间隔
        _this.lastFrameTime = 0; // 上一帧时间
        _this.lastAvgFrameTime = 16; // 最近一段时间的平均帧时间（ms），用于低FPS优化
        // 插值计算缓存
        _this.interpolationCache = new Map();
        _this.cacheValidDuration = 16; // 缓存有效期16ms（约60FPS）
        return _this;
    }
    GamePageController_1 = GamePageController;
    /**
     * 检查本地玩家是否已死亡
     */
    GamePageController.prototype.isLocalPlayerDead = function () {
        var _a, _b;
        // 重连场景下 myPlayerData 可能尚未就绪（尤其是本地玩家已死亡不会生成动物节点），
        // 因此改为直接读取登录 userId 判断。
        var myUserId = (_b = (_a = GlobalBean_1.GlobalBean.GetInstance().loginData) === null || _a === void 0 ? void 0 : _a.userInfo) === null || _b === void 0 ? void 0 : _b.userId;
        if (!myUserId)
            return false;
        return this.fallenPlayers.has(myUserId);
    };
    /**
     * 根据 EnterRoom 的 users 预标记死亡玩家集合。
     * 确保在恢复各阶段(resume*Phase)之前，isLocalPlayerDead() 判断为真时不会短暂启用摇杆。
     */
    GamePageController.prototype.premarkFallenFromEnterRoom = function (users) {
        var _a, _b;
        if (!Array.isArray(users))
            return;
        // 以服务端为准重建死亡集合
        this.fallenPlayers.clear();
        for (var _i = 0, users_1 = users; _i < users_1.length; _i++) {
            var u = users_1[_i];
            if (u && u.bpStatus === 2 && u.userId) {
                this.fallenPlayers.add(u.userId);
            }
        }
        // 若本地玩家已经死亡，立即禁用输入与摇杆，避免任何启用闪烁
        var myUserId = (_b = (_a = GlobalBean_1.GlobalBean.GetInstance().loginData) === null || _a === void 0 ? void 0 : _a.userInfo) === null || _b === void 0 ? void 0 : _b.userId;
        if (myUserId && this.fallenPlayers.has(myUserId)) {
            this.isInputBlocked = true;
            this.isFrameInputStopped = true;
            this.currentPlayerVelocity = cc.Vec2.ZERO;
            if (this.joystickController) {
                this.joystickController.setInteractable(false);
            }
        }
    };
    /**
     * 获取总玩家数量（优先使用已缓存的 allPlayerData）
     */
    GamePageController.prototype.getTotalPlayerCount = function () {
        var _a;
        if (this.allPlayerData && this.allPlayerData.size > 0) {
            return this.allPlayerData.size;
        }
        var users = (_a = GlobalBean_1.GlobalBean.GetInstance().noticeStartGame) === null || _a === void 0 ? void 0 : _a.users;
        return Array.isArray(users) ? users.length : 0;
    };
    /**
     * 获取存活玩家数量（= 总人数 - 已掉落人数）
     */
    GamePageController.prototype.getAlivePlayerCount = function () {
        var total = this.getTotalPlayerCount();
        var fallen = this.fallenPlayers ? this.fallenPlayers.size : 0;
        return Math.max(0, total - fallen);
    };
    GamePageController.prototype.onLoad = function () {
        cc.director.getPhysicsManager().enabled = true;
        // 初始化对象池
        this.initializeObjectPool();
        // 初始化玩家头像状态节点
        this.initializeAllPlayerAvatarStatus();
    };
    GamePageController.prototype.onEnable = function () {
        // 页面激活时的初始化逻辑
    };
    /**
     * 初始化对象池 - 减少GC压力
     */
    GamePageController.prototype.initializeObjectPool = function () {
        // 预创建Vec2对象池
        for (var i = 0; i < this.poolSize; i++) {
            this.vec2Pool.push(cc.v2(0, 0));
        }
    };
    /**
     * 从对象池获取Vec2对象
     */
    GamePageController.prototype.getVec2FromPool = function (x, y) {
        if (x === void 0) { x = 0; }
        if (y === void 0) { y = 0; }
        if (this.vec2Pool.length > 0) {
            var vec = this.vec2Pool.pop();
            vec.x = x;
            vec.y = y;
            return vec;
        }
        return cc.v2(x, y);
    };
    /**
     * 将Vec2对象返回对象池
     */
    GamePageController.prototype.returnVec2ToPool = function (vec) {
        if (this.vec2Pool.length < this.poolSize) {
            vec.x = 0;
            vec.y = 0;
            this.vec2Pool.push(vec);
        }
    };
    /**
     * 将 GameFrame 中的方块事件(3/4/5)转换为 GameEventData 并转发到动画管理器与附加逻辑
     */
    GamePageController.prototype.forwardBlockEventFromFrame = function (frameEvent) {
        var anyEvent = frameEvent;
        var eventType = anyEvent.eventType;
        var evt = anyEvent.eventData || anyEvent;
        var blocksSrc = evt && (evt.blockChanges || evt.blocks);
        var gameEventData = {
            eventType: eventType,
            currentRound: (evt && evt.currentRound) || 1,
            countDown: (evt && evt.countDown) || 0,
            randomSeed: (evt && evt.randomSeed) || Math.floor(Math.random() * 100000),
            message: "",
            blocks: Array.isArray(blocksSrc) ? blocksSrc.map(function (b, idx) { return ({
                id: (typeof b.id === 'number' ? b.id : (idx + 1)),
                x: b.x || 0,
                y: b.y || 0,
                blockType: 1,
                animationState: GameBean_1.BlockAnimationState.NONE,
                animationDelay: 0
            }); }) : []
        };
        // 委托给方块动画管理器
        if (!this.blockAnimationManager) {
            this.initializeBlockAnimationManager();
        }
        if (this.blockAnimationManager) {
            this.blockAnimationManager.handleGameEvent(gameEventData);
        }
        // 执行附加逻辑（更新阶段、fallenBlocks 等）
        this.handleAdditionalEventLogic(gameEventData);
    };
    /**
     * 处理玩家死亡事件（eventType:7）
     * 要求：不影响方块动画(例如 eventType:4 正在进行)，仅针对对应玩家播放掉落动画。
     */
    GamePageController.prototype.handlePlayerDeathEvent = function (gameEvent) {
        if (!gameEvent)
            return;
        var anyEvent = gameEvent;
        var userId = anyEvent.userId || (anyEvent.eventData && anyEvent.eventData.userId);
        if (!userId) {
            console.warn("[GPC] 玩家死亡事件缺少 userId，无法播放掉落动画", gameEvent);
            return;
        }
        // 已经处理过掉落则不重复播放
        if (this.fallenPlayers.has(userId)) {
            return;
        }
        // 查找玩家动物节点
        var animalNode = this.allPlayerAnimals.get(userId);
        if (!animalNode) {
            console.warn("[GPC] \u672A\u627E\u5230\u73A9\u5BB6 " + userId + " \u7684\u52A8\u7269\u8282\u70B9\uFF0C\u65E0\u6CD5\u64AD\u653E\u6389\u843D\u52A8\u753B");
            return;
        }
        // 统一视角：尽量使用服务端权威坐标计算偏移方向，避免各端由于插值/同步差异导致方向不一致
        var evtData = anyEvent.eventData || anyEvent;
        var deathPixelX = typeof (evtData === null || evtData === void 0 ? void 0 : evtData.pixelX) === 'number' ? evtData.pixelX : undefined;
        var deathPixelY = typeof (evtData === null || evtData === void 0 ? void 0 : evtData.pixelY) === 'number' ? evtData.pixelY : undefined;
        // 新增：提取格子内掉落的致死格子坐标
        var deathGridX = typeof (evtData === null || evtData === void 0 ? void 0 : evtData.gridX) === 'number' ? evtData.gridX : undefined;
        var deathGridY = typeof (evtData === null || evtData === void 0 ? void 0 : evtData.gridY) === 'number' ? evtData.gridY : undefined;
        // 播放掉落动画（内部会计算合适的偏移并隐藏bg）
        this.executePlayerFall(userId, {
            forceOffset: true,
            pixelX: deathPixelX,
            pixelY: deathPixelY,
            gridX: deathGridX,
            gridY: deathGridY
        });
    };
    GamePageController.prototype.start = function () {
        var _this = this;
        // 初始化返回按钮
        Tools_1.Tools.imageButtonClick(this.boardBtnBack, Config_1.Config.buttonRes + 'side_btn_back_normal', Config_1.Config.buttonRes + 'side_btn_back_pressed', function () {
            _this.isLeaveGameDialogShow = true;
            _this.leaveDialogController.show(1, function () {
                _this.isLeaveGameDialogShow = false;
            }, function () {
                // 退出游戏时清理视觉元素
                _this.cleanupVisualElements();
                // 关键：确认退出本局后，立即停止一切帧输入的收集与发送
                _this.isFrameInputStopped = true;
                _this.isInputBlocked = true;
                _this.frameInputBuffer = [];
                // 禁用摇杆交互，避免继续产生输入
                if (_this.joystickController) {
                    _this.joystickController.setInteractable(false);
                }
                // 停止本地移动速度，避免视觉残留
                _this.isPlayerMoving = false;
                _this.currentPlayerVelocity = cc.Vec2.ZERO;
            });
        });
        // 绑定 ForceCloseGame 按钮点击事件（若在编辑器中设置了该节点）
        if (this.forceCloseBtn) {
            this.forceCloseBtn.on(cc.Node.EventType.TOUCH_END, this.onClickForceCloseGame, this);
        }
        // 初始化摇杆控制
        this.initializeJoystick();
        // 初始化批量发送时间
        this.lastBatchSendTime = Date.now();
        // 初始化方块动画管理器
        this.initializeBlockAnimationManager();
        // 初始化游戏事件处理器
        this.initializeGameEventHandler();
        // 初始化坐标显示功能
        this.initializeCoordinateDisplay();
        // 初始化倒计时系统
        this.initializeCountdownSystem();
        // 初始化回合数显示系统
        this.initializeRoundDisplaySystem();
        // 初始化jingbao系统
        this.initializeJingbaoSystem();
        // 确保开局隐藏GameOver节点
        this.hideGameOver();
        // 设置infoClick回调，模仿HallPageController的实现方式
        this.infoClick = function () {
            if (_this.infoDialogController) {
                _this.infoDialogController.show(function () { });
            }
        };
        this.menuLayoutController.setClick(function () {
            //弹出游戏简介
            if (_this.infoClick) {
                _this.infoClick();
            }
        });
    };
    /**
     * 点击“强制关闭游戏”按钮，向后端发送 ForceCloseGame 消息
     */
    GamePageController.prototype.onClickForceCloseGame = function () {
        try {
            WebSocketManager_1.WebSocketManager.GetInstance().sendMsg(MessageId_1.MessageId.MsgTypeForceCloseGame, {});
            GameMgr_1.GameMgr.Console.Log('🔧 Sent ForceCloseGame');
        }
        catch (e) {
            cc.warn('Failed to send ForceCloseGame:', e);
        }
    };
    //设置按钮点击事件的回调的
    GamePageController.prototype.setClick = function (backClick, infoClick, settingClick) {
        this.infoClick = infoClick;
    };
    /**
     * 初始化所有玩家的动物预制体（统一管理）
     */
    GamePageController.prototype.initializeAllPlayersAnimals = function () {
        var _this = this;
        var _a, _b;
        var gameStartData = GlobalBean_1.GlobalBean.GetInstance().noticeStartGame;
        if (!gameStartData || !gameStartData.users) {
            console.warn("游戏数据尚未初始化，无法生成动物预制体");
            return;
        }
        // 获取自己的用户ID
        var myUserId = (_b = (_a = GlobalBean_1.GlobalBean.GetInstance().loginData) === null || _a === void 0 ? void 0 : _a.userInfo) === null || _b === void 0 ? void 0 : _b.userId;
        if (!myUserId) {
            console.error("无法获取玩家ID");
            return;
        }
        // 清理旧的动物节点
        this.clearAllAnimals();
        // 延迟一帧再重新生成动物预制体，确保清理完全完成
        this.scheduleOnce(function () {
            _this.createAllPlayerAnimals(gameStartData.users, myUserId);
        }, 0);
    };
    /**
     * 创建所有玩家的动物预制体
     */
    GamePageController.prototype.createAllPlayerAnimals = function (playerDataArray, myUserId) {
        var _this = this;
        var _loop_1 = function (playerData) {
            // 存储玩家数据
            this_1.allPlayerData.set(playerData.userId, playerData);
            // 生成动物预制体
            var animalNode = this_1.createAnimalPrefab(playerData.playerType, playerData.x, playerData.y, playerData.userId, playerData.playerNumber);
            if (animalNode) {
                // 存储动物节点
                this_1.allPlayerAnimals.set(playerData.userId, animalNode);
                // 如果是自己的动物，特别标记
                if (playerData.userId === myUserId) {
                    this_1.myPlayerData = playerData;
                    this_1.myAnimalNode = animalNode;
                    // 若正处于硬冻结（事件1已到，事件2未到），将冻结锚点更新为新生成节点的出生点
                    if (this_1.isLocalFrozen) {
                        this_1.frozenLocalPos = cc.v2(animalNode.x, animalNode.y);
                    }
                    // 开局时显示提示动画（只对本地玩家显示）
                    this_1.scheduleOnce(function () {
                        _this.showTishiAnimation(playerData.userId);
                    }, 0.5); // 延迟0.5秒显示，确保动物动画初始化完成
                }
            }
        };
        var this_1 = this;
        // 为每个玩家生成动物预制体
        for (var _i = 0, playerDataArray_1 = playerDataArray; _i < playerDataArray_1.length; _i++) {
            var playerData = playerDataArray_1[_i];
            _loop_1(playerData);
        }
    };
    /**
     * 统一的GameStart入口：重置状态 -> 创建头像分数界面 -> 初始化动物预制体
     */
    GamePageController.prototype.handleGameStart = function () {
        // 1. 完整重置游戏状态（确保所有方块显示、清理动画、重置同步状态等）
        this.resetGameState();
        // 2. 创建头像和分数界面
        if (this.gameLayoutController) {
            this.gameLayoutController.createScoreView();
        }
        else {
            console.error("gameLayoutController 未设置！请在编辑器中拖拽 GameLayoutController 组件");
        }
        // 3. 初始化所有玩家的动物预制体
        this.initializeAllPlayersAnimals();
        // 4. 设置方块皮肤（在方块动画管理器初始化之后）
        this.setBlockSkinFromMapType();
    };
    /**
     * 根据地图类型设置方块皮肤
     */
    GamePageController.prototype.setBlockSkinFromMapType = function () {
        try {
            var gameStartData = GlobalBean_1.GlobalBean.GetInstance().noticeStartGame;
            if (gameStartData && gameStartData.mapType) {
                var mapType = gameStartData.mapType;
                // 使用全局管理器的静态单例
                var globalMgr = GlobalManagerController_1.default.Instance;
                if (globalMgr && typeof globalMgr.setBlockSkin === 'function') {
                    globalMgr.setBlockSkin(mapType);
                }
                else {
                    console.warn('GlobalManagerController 单例未找到或 setBlockSkin 方法不存在');
                    // 备用方案：直接调用 BlockAnimationManager
                    this.setBlockSkinDirectly(mapType);
                }
            }
            else {
                console.warn('无法获取地图类型，跳过方块皮肤设置');
            }
        }
        catch (e) {
            console.warn('设置方块皮肤失败:', e);
            // 备用方案：直接调用 BlockAnimationManager
            try {
                var gameStartData = GlobalBean_1.GlobalBean.GetInstance().noticeStartGame;
                if (gameStartData && gameStartData.mapType) {
                    this.setBlockSkinDirectly(gameStartData.mapType);
                }
            }
            catch (e2) {
                console.warn('备用方案也失败:', e2);
            }
        }
    };
    /**
     * 直接设置方块皮肤（备用方案）
     */
    GamePageController.prototype.setBlockSkinDirectly = function (mapType) {
        var suffix = mapType === 2 ? '2' : '1';
        var blockSkin = "zhuankuai" + suffix;
        try {
            if (this.blockAnimationManager && typeof this.blockAnimationManager.setBlockSkin === 'function') {
                this.blockAnimationManager.setBlockSkin(blockSkin);
            }
            else {
                console.warn('BlockAnimationManager 未初始化或 setBlockSkin 方法不存在');
            }
        }
        catch (e) {
            console.warn('直接设置方块皮肤失败:', e);
        }
    };
    /**
     * 创建动物预制体
     * @param playerType 玩家类型
     * @param x 初始位置x坐标
     * @param y 初始位置y坐标
     * @param userId 玩家ID
     * @param playerNumber 玩家位置编号
     * @returns 创建的动物节点
     */
    GamePageController.prototype.createAnimalPrefab = function (playerType, x, y, userId, playerNumber) {
        if (!this.animalPrefab) {
            console.error("动物预制体未设置");
            return null;
        }
        // 检查动物类型是否有效
        if (!this.ANIMAL_TYPES[playerType]) {
            console.error("\u672A\u77E5\u7684playerType: " + playerType);
            return null;
        }
        // 实例化统一动物预制体
        var animalNode = cc.instantiate(this.animalPrefab);
        // 计算安全出生点：当坐标无效或落在默认中心(0,0)时，按座位号落到四角
        var isValidNum = function (v) { return typeof v === 'number' && !Number.isNaN(v) && Number.isFinite(v); };
        var spawnX = isValidNum(x) ? x : Number.NaN;
        var spawnY = isValidNum(y) ? y : Number.NaN;
        var isCenter = function (vx, vy) { return isValidNum(vx) && isValidNum(vy) && Math.abs(vx) < 1e-3 && Math.abs(vy) < 1e-3; };
        if (!isValidNum(spawnX) || !isValidNum(spawnY) || isCenter(spawnX, spawnY)) {
            // 使用服务端规范的默认出生点（单位：像素）
            switch (playerNumber) {
                case 1: // 左上
                    spawnX = -240;
                    spawnY = 258;
                    break;
                case 2: // 右上
                    spawnX = 240;
                    spawnY = 258;
                    break;
                case 3: // 左下
                    spawnX = -240;
                    spawnY = -225;
                    break;
                default: // 4或其他 -> 右下
                    spawnX = 240;
                    spawnY = -255;
                    break;
            }
            cc.warn("[Spawn] \u65E0\u6548\u6216\u7F3A\u5931\u5750\u6807\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u51FA\u751F\u70B9 userId=" + userId + ", playerNumber=" + playerNumber + ", fallback=(" + spawnX.toFixed(1) + ", " + spawnY.toFixed(1) + ")", { x: x, y: y });
        }
        // 设置初始位置
        animalNode.setPosition(spawnX, spawnY);
        // 设置初始朝向：1号位和2号位（上方位置）的动物头朝下
        if (playerNumber === 1 || playerNumber === 2) {
            animalNode.angle = 180;
        }
        // 添加到场景中
        if (this.playerNode) {
            this.playerNode.addChild(animalNode);
        }
        else {
            this.node.addChild(animalNode);
        }
        // 设置bg节点可见性（只有本地玩家显示bg节点）
        this.setupBgNodeVisibility(animalNode, userId);
        // 初始化动画系统
        this.initializeAnimalAnimation(animalNode, playerType, userId);
        return animalNode;
    };
    /**
     * 设置bg节点可见性（只有本地玩家显示bg节点）
     * @param animalNode 动物节点
     * @param userId 玩家ID
     */
    GamePageController.prototype.setupBgNodeVisibility = function (animalNode, userId) {
        var _a, _b;
        var bgNode = animalNode.getChildByName('bg');
        if (bgNode) {
            // 获取本地玩家ID
            var myUserId = (_b = (_a = GlobalBean_1.GlobalBean.GetInstance().loginData) === null || _a === void 0 ? void 0 : _a.userInfo) === null || _b === void 0 ? void 0 : _b.userId;
            var isLocalPlayer = (myUserId === userId);
            bgNode.active = isLocalPlayer;
        }
        else {
            console.warn("\u52A8\u7269\u8282\u70B9\u4E2D\u672A\u627E\u5230bg\u5B50\u8282\u70B9");
        }
    };
    // 已删除showStartCountdown方法 - 倒计时统一由GameEventWaiting消息触发
    /**
     * 根据 EnterRoom 数据初始化客户端帧ID，使断线重连后的 FrameInput 帧号从后端给定数字开始。
     * 兼容多种后端字段：
     * - nextClientFrameId / clientFrameStart / clientStartFrameId / startFrameId / nextFrameId
     * - clientFrameId（API更新后的新字段，表示断线前最新的客户端帧ID，则下一帧为 +1）
     * - currentClientFrameId（表示当前已用的最后一帧，则下一帧为 +1）
     * - lastClientFrameId（表示最后一帧号，则下一帧为 +1）
     */
    GamePageController.prototype.initializeClientFrameFromReconnection = function (enterRoomData) {
        var startId = null;
        var d = enterRoomData || {};
        // 直接表示“下一帧”的优先
        if (typeof d.nextClientFrameId === 'number')
            startId = d.nextClientFrameId;
        else if (typeof d.clientFrameStart === 'number')
            startId = d.clientFrameStart;
        else if (typeof d.clientStartFrameId === 'number')
            startId = d.clientStartFrameId;
        else if (typeof d.startFrameId === 'number')
            startId = d.startFrameId;
        else if (typeof d.nextFrameId === 'number')
            startId = d.nextFrameId;
        // 通过“当前/最后一帧”推导下一帧
        if (startId === null) {
            if (typeof d.clientFrameId === 'number')
                startId = d.clientFrameId + 1; // 新增：API更新后的字段
            else if (typeof d.currentClientFrameId === 'number')
                startId = d.currentClientFrameId + 1;
            else if (typeof d.lastClientFrameId === 'number')
                startId = d.lastClientFrameId + 1;
        }
        // 兜底：不做修改，仅记录日志
        if (startId === null) {
            console.warn('[Reconnection] 未从 EnterRoom 中解析到客户端起始帧号，保持现有 clientFrameId:', this.clientFrameId);
            return;
        }
        // 使下一次 ++clientFrameId 等于后端给定的 startId
        this.clientFrameId = startId - 1;
        // 清空并重置缓冲与计时（避免用旧帧号/旧时间片）
        this.frameInputBuffer = [];
        this.lastBatchSendTime = Date.now();
    };
    /**
     * 【修复1】掉落动画触发前隐藏bg节点，防止动画消失
     * @param animalNode 动物节点
     * @param userId 玩家ID
     */
    GamePageController.prototype.hideBgNodeBeforeFall = function (animalNode, userId) {
        var _a, _b;
        var bgNode = animalNode.getChildByName('bg');
        if (bgNode) {
            // 获取本地玩家ID
            var myUserId = (_b = (_a = GlobalBean_1.GlobalBean.GetInstance().loginData) === null || _a === void 0 ? void 0 : _a.userInfo) === null || _b === void 0 ? void 0 : _b.userId;
            var isLocalPlayer = (myUserId === userId);
            // 只有本地玩家才需要隐藏bg节点（因为只有本地玩家的bg节点是可见的）
            if (isLocalPlayer) {
                // 掉落动画触发前隐藏bg节点，防止与掉落动画冲突
                bgNode.active = false;
            }
        }
    };
    /**
     * 初始化动物动画系统
     * @param animalNode 动物节点
     * @param playerType 玩家类型
     * @param userId 玩家ID
     */
    GamePageController.prototype.initializeAnimalAnimation = function (animalNode, playerType, userId) {
        // 获取dongwu子节点上的骨骼动画组件
        var dongwuNode = animalNode.getChildByName('dongwu');
        if (!dongwuNode) {
            console.error("\u52A8\u7269\u8282\u70B9\u4E2D\u672A\u627E\u5230dongwu\u5B50\u8282\u70B9");
            return;
        }
        var skeletonComponent = dongwuNode.getComponent(sp.Skeleton);
        if (!skeletonComponent) {
            console.error("dongwu\u8282\u70B9\u7F3A\u5C11Skeleton\u7EC4\u4EF6");
            return;
        }
        // 检查骨骼动画数据是否加载完成
        if (!skeletonComponent.skeletonData) {
            console.error("Skeleton\u6570\u636E\u672A\u52A0\u8F7D");
            return;
        }
        // 存储骨骼动画组件引用
        this.animalAnimations.set(userId, skeletonComponent);
        // 初始化提示动画系统
        this.initializeTishiAnimation(animalNode, userId);
        // 获取动物类型
        var animalType = this.ANIMAL_TYPES[playerType];
        if (!animalType) {
            console.error("\u65E0\u6548\u7684\u73A9\u5BB6\u7C7B\u578B: " + playerType);
            return;
        }
        // 设置初始动画状态为静止
        var initialAnimationName = this.ANIMATION_STATES.STATIC + "/" + animalType;
        // 初始化计时器和状态
        this.lastMovementTimes.set(userId, Date.now());
        this.currentAnimationStates.set(userId, this.ANIMATION_STATES.STATIC);
        // 延迟播放动画，确保骨骼动画完全初始化
        this.tryPlayInitialAnimation(userId, initialAnimationName, 0);
    };
    /**
     * 尝试播放初始动画（带重试机制）
     * @param userId 玩家ID
     * @param animationName 动画名称
     * @param retryCount 重试次数
     */
    GamePageController.prototype.tryPlayInitialAnimation = function (userId, animationName, retryCount) {
        var _this = this;
        var maxRetries = 5;
        var delayTime = 0.1 + retryCount * 0.1; // 0.1s, 0.2s, 0.3s, 0.4s, 0.5s
        this.scheduleOnce(function () {
            var skeletonComponent = _this.animalAnimations.get(userId);
            if (!skeletonComponent) {
                return;
            }
            // 检查动画是否存在
            if (skeletonComponent.skeletonData) {
                try {
                    var animationData = skeletonComponent.findAnimation(animationName);
                    if (animationData) {
                        // 动画存在，播放它
                        _this.playAnimalAnimation(userId, animationName, true);
                        return;
                    }
                }
                catch (e) {
                    // 继续重试
                }
            }
            // 动画不存在，尝试重试
            if (retryCount < maxRetries - 1) {
                _this.tryPlayInitialAnimation(userId, animationName, retryCount + 1);
            }
            else {
                // 尝试播放一个可能存在的默认动画
                _this.tryPlayFallbackAnimation(userId);
            }
        }, delayTime);
    };
    /**
     * 尝试播放备用动画
     * @param userId 玩家ID
     */
    GamePageController.prototype.tryPlayFallbackAnimation = function (userId) {
        var skeletonComponent = this.animalAnimations.get(userId);
        if (!skeletonComponent || !skeletonComponent.skeletonData) {
            return;
        }
        // 尝试一些常见的动画名称格式
        var fallbackAnimations = [
            'jingzhi',
            'idle',
            'static',
            'default' // 默认动画
        ];
        for (var _i = 0, fallbackAnimations_1 = fallbackAnimations; _i < fallbackAnimations_1.length; _i++) {
            var animName = fallbackAnimations_1[_i];
            try {
                var animationData = skeletonComponent.findAnimation(animName);
                if (animationData) {
                    this.playAnimalAnimation(userId, animName, true);
                    return;
                }
            }
            catch (e) {
                // 继续尝试下一个
            }
        }
    };
    /**
     * 初始化提示动画系统
     * @param animalNode 动物节点
     * @param userId 玩家ID
     */
    GamePageController.prototype.initializeTishiAnimation = function (animalNode, userId) {
        // 查找tishi子节点
        var tishiNode = animalNode.getChildByName('tishi');
        if (!tishiNode) {
            return;
        }
        // 获取tishi节点上的骨骼动画组件
        var tishiSkeletonComponent = tishiNode.getComponent(sp.Skeleton);
        if (!tishiSkeletonComponent) {
            console.warn("tishi\u8282\u70B9\u7F3A\u5C11Skeleton\u7EC4\u4EF6");
            return;
        }
        // 检查骨骼动画数据是否加载完成
        if (!tishiSkeletonComponent.skeletonData) {
            console.warn("tishi\u8282\u70B9Skeleton\u6570\u636E\u672A\u52A0\u8F7D");
            return;
        }
        // 存储提示动画组件和节点引用
        this.tishiAnimations.set(userId, tishiSkeletonComponent);
        this.tishiNodes.set(userId, tishiNode);
        // 初始时隐藏提示节点
        tishiNode.active = false;
    };
    /**
     * 显示提示动画（只对本地玩家）
     * @param userId 玩家ID
     */
    GamePageController.prototype.showTishiAnimation = function (userId) {
        var tishiNode = this.tishiNodes.get(userId);
        var tishiSkeletonComponent = this.tishiAnimations.get(userId);
        if (!tishiNode || !tishiSkeletonComponent) {
            return;
        }
        try {
            // 显示提示节点
            tishiNode.active = true;
            // 播放提示动画
            tishiSkeletonComponent.setAnimation(0, "tishi", true); // 循环播放
        }
        catch (error) {
            console.error("\u64AD\u653E\u63D0\u793A\u52A8\u753B\u5931\u8D25:", error);
        }
    };
    /**
     * 隐藏提示动画
     * @param userId 玩家ID，如果不传则隐藏所有玩家的提示
     */
    GamePageController.prototype.hideTishiAnimation = function (userId) {
        if (userId) {
            // 隐藏指定玩家的提示
            var tishiNode = this.tishiNodes.get(userId);
            if (tishiNode) {
                tishiNode.active = false;
            }
        }
        else {
            // 隐藏所有玩家的提示
            this.tishiNodes.forEach(function (tishiNode, playerId) {
                if (tishiNode) {
                    tishiNode.active = false;
                }
            });
        }
    };
    /**
     * 播放动物动画
     * @param userId 玩家ID
     * @param animationName 动画名称
     * @param loop 是否循环播放
     */
    GamePageController.prototype.playAnimalAnimation = function (userId, animationName, loop) {
        if (loop === void 0) { loop = false; }
        var skeletonComponent = this.animalAnimations.get(userId);
        if (!skeletonComponent) {
            console.warn("\u73A9\u5BB6 " + userId + " \u7684\u9AA8\u9ABC\u52A8\u753B\u7EC4\u4EF6\u4E0D\u5B58\u5728");
            return;
        }
        try {
            // 播放骨骼动画
            skeletonComponent.setAnimation(0, animationName, loop);
        }
        catch (error) {
            console.error("\u64AD\u653E\u9AA8\u9ABC\u52A8\u753B\u5931\u8D25 " + animationName + ":", error);
        }
    };
    /**
     * 更新玩家动画状态
     * @param userId 玩家ID
     * @param isMoving 是否正在移动
     */
    GamePageController.prototype.updatePlayerAnimationState = function (userId, isMoving) {
        var playerData = this.allPlayerData.get(userId);
        if (!playerData)
            return;
        var animalType = this.ANIMAL_TYPES[playerData.playerType];
        if (!animalType)
            return;
        var currentState = this.currentAnimationStates.get(userId);
        var currentTime = Date.now();
        if (isMoving) {
            // 正在移动，播放行走动画
            this.lastMovementTimes.set(userId, currentTime);
            if (currentState !== this.ANIMATION_STATES.WALK) {
                var walkAnimationName = this.ANIMATION_STATES.WALK + "/" + animalType;
                this.playAnimalAnimation(userId, walkAnimationName, true);
                this.currentAnimationStates.set(userId, this.ANIMATION_STATES.WALK);
            }
        }
        else {
            // 停止移动
            var lastMovementTime = this.lastMovementTimes.get(userId) || currentTime;
            var timeSinceLastMovement = currentTime - lastMovementTime;
            if (currentState === this.ANIMATION_STATES.WALK) {
                // 从行走状态切换到静止状态
                var staticAnimationName = this.ANIMATION_STATES.STATIC + "/" + animalType;
                this.playAnimalAnimation(userId, staticAnimationName, true);
                this.currentAnimationStates.set(userId, this.ANIMATION_STATES.STATIC);
            }
            else if (currentState === this.ANIMATION_STATES.STATIC && timeSinceLastMovement >= this.IDLE_TIMEOUT) {
                // 静止超过3秒，播放待机动画
                var idleAnimationName = this.ANIMATION_STATES.IDLE + "/" + animalType;
                this.playAnimalAnimation(userId, idleAnimationName, false);
                this.currentAnimationStates.set(userId, this.ANIMATION_STATES.IDLE);
                // 待机动画播放完成后回到静止状态
                this.scheduleIdleAnimationComplete(userId, animalType);
            }
        }
    };
    /**
     * 安排待机动画完成后的处理
     * @param userId 玩家ID
     * @param animalType 动物类型
     */
    GamePageController.prototype.scheduleIdleAnimationComplete = function (userId, animalType) {
        var _this = this;
        var skeletonComponent = this.animalAnimations.get(userId);
        if (!skeletonComponent)
            return;
        // 监听骨骼动画完成事件
        skeletonComponent.setCompleteListener(function () {
            // 待机动画完成，回到静止状态
            var staticAnimationName = _this.ANIMATION_STATES.STATIC + "/" + animalType;
            _this.playAnimalAnimation(userId, staticAnimationName, true);
            _this.currentAnimationStates.set(userId, _this.ANIMATION_STATES.STATIC);
            // 清理监听器
            skeletonComponent.setCompleteListener(null);
        });
    };
    /**
     * 清理所有动物节点
     */
    GamePageController.prototype.clearAllAnimals = function () {
        // 销毁所有动物节点
        this.allPlayerAnimals.forEach(function (animalNode) {
            if (animalNode && cc.isValid(animalNode)) {
                animalNode.destroy();
            }
        });
        // 清理数据
        this.allPlayerAnimals.clear();
        this.allPlayerData.clear();
        this.myAnimalNode = null;
        this.myPlayerData = null;
        // 清理动画相关数据
        this.animalAnimations.clear();
        this.lastMovementTimes.clear();
        this.currentAnimationStates.clear();
        // 清理提示动画相关数据
        this.tishiAnimations.clear();
        this.tishiNodes.clear();
        // 清理待机动画检查定时器
        this.clearIdleAnimationTimer();
    };
    /**
     * 获取指定玩家的动物节点
     * @param userId 玩家ID
     * @returns 动物节点
     */
    GamePageController.prototype.getPlayerAnimal = function (userId) {
        return this.allPlayerAnimals.get(userId);
    };
    /**
     * 获取所有玩家的动物节点
     * @returns 所有玩家动物节点的Map
     */
    GamePageController.prototype.getAllPlayerAnimals = function () {
        return this.allPlayerAnimals;
    };
    /**
     * 初始化摇杆控制
     */
    GamePageController.prototype.initializeJoystick = function () {
        var _this = this;
        if (!this.joystickController) {
            console.warn("摇杆控制器未设置，跳过摇杆初始化");
            return;
        }
        // 启用点击生成模式
        this.joystickController.enableClickToGenerate = true;
        // 设置摇杆开始回调
        this.joystickController.setOnJoystickStart(function (_inputData) {
            // 检查本地玩家是否死亡，死亡后不允许移动
            if (_this.isLocalPlayerDead()) {
                _this.isPlayerMoving = false;
                _this.currentPlayerVelocity = cc.Vec2.ZERO;
                return;
            }
            // 仅依赖摇杆可交互状态来决定是否允许开始移动
            if (!_this.joystickController.isInteractable()) {
                _this.isPlayerMoving = false;
                _this.currentPlayerVelocity = cc.Vec2.ZERO;
                return;
            }
            _this.isPlayerMoving = true;
        });
        // 设置摇杆移动回调
        this.joystickController.setOnJoystickMove(function (inputData) {
            // 检查本地玩家是否死亡，死亡后不允许移动
            if (_this.isLocalPlayerDead()) {
                _this.currentPlayerVelocity = cc.Vec2.ZERO;
                return;
            }
            // 仅依赖摇杆可交互状态
            if (!_this.joystickController.isInteractable()) {
                _this.currentPlayerVelocity = cc.Vec2.ZERO;
                return;
            }
            // 计算玩家移动速度
            var effectiveSpeed = _this.playerMoveSpeed * _this.moveSpeedScale;
            // 注意：VirtualJoystickController 中的 inputData.x / y 已经包含了力度（force），
            // 即 x = direction.x * force，y = direction.y * force，范围均为 [-1,1]
            // 因此前端预测应使用 x/y 直接乘以速度，不能再额外乘 force，否则会导致速度被平方(f^2)
            // 与后端的 UpdatePlayerPosition 保持一致：new = current + input * effectiveSpeed * dt
            _this.currentPlayerVelocity.x = inputData.x * effectiveSpeed;
            _this.currentPlayerVelocity.y = inputData.y * effectiveSpeed;
        });
        // 设置摇杆结束回调
        this.joystickController.setOnJoystickEnd(function () {
            _this.isPlayerMoving = false;
            _this.currentPlayerVelocity = cc.Vec2.ZERO;
        });
    };
    //结算
    GamePageController.prototype.setCongratsDialog = function (noticeSettlement) {
        var _this = this;
        // 收到结算后立即停止一切输入发送，避免房间已销毁仍发送FrameInput
        this.isFrameInputStopped = true;
        this.isInputBlocked = true;
        this.frameInputBuffer = [];
        if (this.joystickController) {
            this.joystickController.setInteractable(false);
        }
        // 结算时清理视觉元素
        this.cleanupVisualElements();
        this.setCongrats(noticeSettlement);
        //退出弹窗正在显示的话  就先关闭
        if (this.isLeaveGameDialogShow) {
            this.leaveDialogController.hide();
        }
        this.isCongratsDialog = true;
        //弹出结算弹窗
        this.congratsDialogController.show(noticeSettlement, function () {
            _this.isCongratsDialog = false;
        });
    };
    GamePageController.prototype.onDisable = function () {
        //退出弹窗正在显示的话  就先关闭
        if (this.isLeaveGameDialogShow) {
            this.leaveDialogController.hide();
        }
        //结算弹窗正在显示的话就先关闭掉
        if (this.isCongratsDialog) {
            this.congratsDialogController.hide();
        }
    };
    //结算
    GamePageController.prototype.setCongrats = function (noticeSettlement) {
        var index = noticeSettlement.users.findIndex(function (item) { return item.userId === GlobalBean_1.GlobalBean.GetInstance().loginData.userInfo.userId; }); //搜索 
        if (index >= 0) { //自己参与的话 才会显示正常的胜利和失败的音效，自己不参与的话 就全部显示胜利的音效
            if (noticeSettlement.users[index].rank === 1) { //判断自己是不是第一名
                AudioManager_1.AudioManager.winAudio();
                this.particle.active = true;
                this.particle1.active = true;
                if (this.particle) {
                    this.particle.getComponent(cc.ParticleSystem).resetSystem();
                }
                if (this.particle1) {
                    this.particle1.getComponent(cc.ParticleSystem).resetSystem();
                }
            }
            else {
                AudioManager_1.AudioManager.loseAudio();
                this.particle.active = false;
                this.particle1.active = false;
            }
        }
        else {
            AudioManager_1.AudioManager.winAudio();
            this.particle.active = true;
            this.particle1.active = true;
            if (this.particle) {
                this.particle.getComponent(cc.ParticleSystem).resetSystem();
            }
            if (this.particle1) {
                this.particle1.getComponent(cc.ParticleSystem).resetSystem();
            }
        }
    };
    /**
     * 每帧更新 - 分离逻辑帧和渲染帧，实现丝滑渲染插值
     */
    GamePageController.prototype.update = function (dt) {
        // 收集输入数据（移除dt依赖）
        this.collectFrameInput();
        // 检查并发送批量输入
        this.checkAndSendBatchInput();
        // 更新逻辑帧计时器
        this.logicFrameTimer += dt;
        // 执行逻辑帧（固定10ms间隔，100FPS - 确保50ms内产生5帧）
        while (this.logicFrameTimer >= this.fixedDeltaTime) {
            this.executeLogicFrame();
            this.logicFrameTimer -= this.fixedDeltaTime;
            this.currentLogicFrame++;
        }
        // 【关键】本地玩家即时移动更新 - 客户端预测
        this.updateLocalPlayerMovement(dt);
        // 【关键】平滑移动更新 - 让其他玩家向目标位置平滑移动
        this.updateSmoothMovement(dt);
        // 【关键】渲染插值更新 - 让后端权威位置在60/120FPS下丝滑显示
        this.updateRenderInterpolation(dt);
        // 硬冻结：在等待期间（1~2之间）强制把本地玩家拉回冻结点，彻底杜绝一切位移来源
        if (this.isLocalFrozen && this.myAnimalNode && cc.isValid(this.myAnimalNode)) {
            this.myAnimalNode.x = this.frozenLocalPos.x;
            this.myAnimalNode.y = this.frozenLocalPos.y;
        }
        // 更新调试信息（FPS/网络/和解等）
        this.updateDebugInfo(dt);
    };
    /**
     * 【核心】本地玩家即时移动更新 - 智能碰撞处理策略
     */
    GamePageController.prototype.updateLocalPlayerMovement = function (dt) {
        // 渲染帧不再推进本地位置，改为仅更新动画和朝向，避免与固定步长重复推进
        if (!this.myAnimalNode || !cc.isValid(this.myAnimalNode) || !this.myPlayerData) {
            return;
        }
        // 仅在可交互且未被阻止输入时，更新朝向/动画（不移动位置）
        var canInteract = !!this.joystickController && this.joystickController.isInteractable() && !this.isInputBlocked && !this.isLocalPlayerDead();
        if (!canInteract) {
            return;
        }
        if (this.currentPlayerVelocity.mag() > 0.01) {
            // 更新朝向与动画表现（不做位置加法）
            this.updateAnimalRotation(this.myAnimalNode, this.currentPlayerVelocity, dt);
            this.updatePlayerAnimationState(this.myPlayerData.userId, true);
        }
        else {
            this.consecutiveCollisionFrames = 0;
            this.updatePlayerAnimationState(this.myPlayerData.userId, false);
        }
    };
    /**
     * 优化的和解机制 - 减少回滚卡顿
     */
    GamePageController.prototype.applyOptimizedReconciliation = function (dt, noInput) {
        if (noInput === void 0) { noInput = false; }
        if (!this.myAnimalNode || !this.lastServerPosition)
            return;
        var currentTime = Date.now();
        var localPos = this.myAnimalNode.getPosition();
        // 使用对象池获取向量，避免频繁创建
        var localVec = this.getVec2FromPool(localPos.x, localPos.y);
        var delta = this.getVec2FromPool(this.lastServerPosition.x - localVec.x, this.lastServerPosition.y - localVec.y);
        var distance = Math.sqrt(delta.x * delta.x + delta.y * delta.y);
        // 碰撞跟随：直接对齐到权威位置，避免误差累积
        if (this.isCollisionFollowing) {
            this.myAnimalNode.x = this.lastServerPosition.x;
            this.myAnimalNode.y = this.lastServerPosition.y;
            // 返回向量到对象池
            this.returnVec2ToPool(localVec);
            this.returnVec2ToPool(delta);
            return;
        }
        // 安全保护：误差极大时直接对齐，防止发散
        if (distance > 200) {
            this.myAnimalNode.x = this.lastServerPosition.x;
            this.myAnimalNode.y = this.lastServerPosition.y;
            this.returnVec2ToPool(localVec);
            this.returnVec2ToPool(delta);
            return;
        }
        // 动态调整校正强度
        var correctionStrength = noInput ? this.reconciliationSmoothness * 0.7 : this.reconciliationSmoothness;
        // 根据距离调整校正强度 - 距离越远，校正越温和
        if (distance > 15) {
            correctionStrength *= 0.6; // 远距离时更温和
        }
        else if (distance > 8) {
            correctionStrength *= 0.8; // 中距离时稍微温和
        }
        // 根据和解频率动态调整 - 更激进的降低（碰撞跟随下跳过此弱化）
        var timeSinceLastReconciliation = currentTime - this.lastReconciliationTime;
        if (!this.isCollisionFollowing) {
            if (timeSinceLastReconciliation < 1000) { // 1秒内有和解
                correctionStrength *= 0.3; // 大幅降低校正强度
            }
            else if (timeSinceLastReconciliation < 2000) { // 2秒内有和解
                correctionStrength *= 0.6; // 适度降低校正强度
            }
        }
        // 只有在误差足够大时才进行校正
        if (distance > 1.0) {
            var correction = this.getVec2FromPool(delta.x * correctionStrength, delta.y * correctionStrength);
            this.myAnimalNode.x = localPos.x + correction.x;
            this.myAnimalNode.y = localPos.y + correction.y;
            // 打印一次性纠正日志（节流）
            var now = Date.now();
            if (now - this.lastErrorLogTime > 200) {
                this.lastErrorLogTime = now;
            }
            // 返回向量到对象池
            this.returnVec2ToPool(correction);
        }
        // 返回向量到对象池
        this.returnVec2ToPool(localVec);
        this.returnVec2ToPool(delta);
    };
    /**
     * 激进策略：积极预测移动，边缘滑动
     */
    GamePageController.prototype.handleAggressiveMovement = function (currentPos, predictedX, predictedY, displacement) {
        // 碰撞时：取消一切本地碰撞解算（不滑、不缩步），进入和解等待后端权威位置
        if (this.checkLocalCollision(predictedX, predictedY)) {
            this.consecutiveCollisionFrames++;
            if (this.myPlayerData) {
                this.reconciliationStates.set(this.myPlayerData.userId, true);
            }
            // 不改变当前位置，但保持行走动画反馈输入
            this.updatePlayerAnimationState(this.myPlayerData.userId, true);
            return;
        }
        // 无碰撞：正常应用预测位移
        this.consecutiveCollisionFrames = 0;
        this.myAnimalNode.x = predictedX;
        this.myAnimalNode.y = predictedY;
        this.updateAnimalRotation(this.myAnimalNode, this.currentPlayerVelocity, GamePageController_1.LOGIC_FRAME_DT);
        this.updatePlayerAnimationState(this.myPlayerData.userId, true);
    };
    /**
     * 保守策略：当检测到碰撞时，限制移动幅度
     */
    GamePageController.prototype.handleConservativeMovement = function (currentPos, predictedX, predictedY, displacement) {
        // 发生碰撞即取消本地预测，不再尝试缩步/安全位移，等待后端权威位置
        if (this.checkLocalCollision(predictedX, predictedY)) {
            this.consecutiveCollisionFrames++;
            if (this.myPlayerData) {
                this.reconciliationStates.set(this.myPlayerData.userId, true);
            }
            // 不移动，但保持行走动画反馈
            this.updatePlayerAnimationState(this.myPlayerData.userId, true);
            return;
        }
        // 无碰撞：按预测位移前进（仍然保持“保守策略”的旋转/动画一致性）
        this.consecutiveCollisionFrames = 0;
        this.myAnimalNode.x = predictedX;
        this.myAnimalNode.y = predictedY;
        this.updateAnimalRotation(this.myAnimalNode, this.currentPlayerVelocity, GamePageController_1.LOGIC_FRAME_DT);
        this.updatePlayerAnimationState(this.myPlayerData.userId, true);
    };
    /**
     * 混合策略：根据连续碰撞情况动态调整
     */
    GamePageController.prototype.handleHybridMovement = function (currentPos, predictedX, predictedY, displacement) {
        // 如果连续碰撞次数较多，采用保守策略
        if (this.consecutiveCollisionFrames > 10) {
            this.handleConservativeMovement(currentPos, predictedX, predictedY, displacement);
            return;
        }
        // 否则使用激进策略
        this.handleAggressiveMovement(currentPos, predictedX, predictedY, displacement);
    };
    /**
     * 前端碰撞检测 - 完全匹配后端AABB碰撞检测算法
     * 玩家体积为60*60像素，坐标为中心点
     */
    GamePageController.prototype.checkLocalCollision = function (predictedX, predictedY) {
        var _this = this;
        var playerSize = 70.0; // 玩家体积60*60，与后端保持一致
        var halfSize = playerSize / 2.0; // 30.0
        // 计算当前玩家新位置的碰撞箱（AABB）
        var currentLeft = predictedX - halfSize;
        var currentRight = predictedX + halfSize;
        var currentTop = predictedY + halfSize;
        var currentBottom = predictedY - halfSize;
        // 检查与所有其他存活玩家的碰撞
        var hasCollision = false;
        this.allPlayerAnimals.forEach(function (otherAnimalNode, userId) {
            // 跳过自己
            if (_this.myPlayerData && userId === _this.myPlayerData.userId) {
                return;
            }
            // 跳过已掉落的玩家（对应后端的BPStatus检查）
            if (_this.fallenPlayers.has(userId)) {
                return;
            }
            // 获取其他玩家的当前位置
            var otherPos = otherAnimalNode.getPosition();
            // 计算其他玩家的碰撞箱
            var otherLeft = otherPos.x - halfSize;
            var otherRight = otherPos.x + halfSize;
            var otherTop = otherPos.y + halfSize;
            var otherBottom = otherPos.y - halfSize;
            // AABB碰撞检测算法（与后端完全一致）
            if (currentLeft < otherRight && currentRight > otherLeft &&
                currentTop > otherBottom && currentBottom < otherTop) {
                // 发生碰撞
                hasCollision = true;
            }
        });
        return hasCollision;
    };
    /**
     * 寻找有效的移动位置 - 沿着碰撞边缘滑动
     */
    GamePageController.prototype.findValidMovePosition = function (currentX, currentY, displacement) {
        // 尝试只在X轴移动
        var xOnlyX = currentX + displacement.x;
        var xOnlyY = currentY;
        if (!this.checkLocalCollision(xOnlyX, xOnlyY)) {
            return cc.v2(xOnlyX, xOnlyY);
        }
        // 尝试只在Y轴移动
        var yOnlyX = currentX;
        var yOnlyY = currentY + displacement.y;
        if (!this.checkLocalCollision(yOnlyX, yOnlyY)) {
            return cc.v2(yOnlyX, yOnlyY);
        }
        // 尝试减少移动距离（50%的位移）
        var halfX = currentX + displacement.x * 0.5;
        var halfY = currentY + displacement.y * 0.5;
        if (!this.checkLocalCollision(halfX, halfY)) {
            return cc.v2(halfX, halfY);
        }
        // 无法移动
        return null;
    };
    /**
     * 记录碰撞上下文信息，用于调试前后端差异
     */
    GamePageController.prototype.logCollisionContext = function (frontendX, frontendY, serverX, serverY) {
        var _this = this;
        // 检查前端是否检测到碰撞
        var frontendCollision = this.checkLocalCollision(frontendX, frontendY);
        var serverCollision = this.checkLocalCollision(serverX, serverY);
        // 记录其他玩家位置
        this.allPlayerAnimals.forEach(function (animalNode, userId) {
            if (_this.myPlayerData && userId === _this.myPlayerData.userId)
                return;
            if (_this.fallenPlayers.has(userId))
                return;
            var pos = animalNode.getPosition();
            var distance = Math.sqrt(Math.pow(frontendX - pos.x, 2) + Math.pow(frontendY - pos.y, 2));
        });
    };
    /**
     * 记录和解事件，并根据频率调整策略
     */
    GamePageController.prototype.recordReconciliation = function () {
        var _this = this;
        var currentTime = Date.now();
        // 碰撞跟随后端期间的和解不计入频率统计，避免策略被动降级
        if (this.isCollisionFollowing) {
            return;
        }
        this.reconciliationHistory.push(currentTime);
        // 保持历史记录在限制范围内
        if (this.reconciliationHistory.length > this.MAX_RECONCILIATION_HISTORY) {
            this.reconciliationHistory.shift();
        }
        // 检查是否频繁和解（最近10秒内）
        var recentReconciliations = this.reconciliationHistory.filter(function (time) {
            return currentTime - time < 10000;
        } // 10秒内
        );
        if (recentReconciliations.length >= this.FREQUENT_RECONCILIATION_THRESHOLD) {
            this.collisionStrategy = 'conservative';
            // 5秒后重置为混合策略
            setTimeout(function () {
                _this.collisionStrategy = 'hybrid';
            }, 5000);
        }
    };
    /**
     * 公开方法：手动切换碰撞策略（调试用）
     */
    GamePageController.prototype.setCollisionStrategy = function (strategy) {
        this.collisionStrategy = strategy;
    };
    /**
     * 公开方法：获取当前碰撞策略状态
     */
    GamePageController.prototype.getCollisionStatus = function () {
        return {
            strategy: this.collisionStrategy,
            consecutiveCollisions: this.consecutiveCollisionFrames,
            recentReconciliations: this.reconciliationHistory.length,
            reconciliationHistory: this.reconciliationHistory.slice(-5) // 最近5次
        };
    };
    // ========== 倒计时系统实现 ==========
    /**
     * 隐藏回合数显示
     */
    GamePageController.prototype.hideRoundDisplay = function () {
        if (this.roundDisplayNode) {
            this.roundDisplayNode.active = false;
        }
    };
    /**
     * 获取多语言文本（简化版本）
     * @param key 多语言key
     * @param params 参数数组
     * @returns 本地化文本
     */
    GamePageController.prototype.getI18nText = function (key, params) {
        if (params === void 0) { params = []; }
        // 这里应该集成真正的多语言系统，现在使用简化版本
        var defaultTexts = {
            'round_display': '第{0}回合' // 默认中文
        };
        var text = defaultTexts[key] || key;
        // 替换参数
        params.forEach(function (param, index) {
            text = text.replace("{" + index + "}", param);
        });
        return text;
    };
    /**
     * 初始化倒计时系统
     */
    GamePageController.prototype.initializeCountdownSystem = function () {
        if (!this.countdownNode) {
            console.warn("倒计时节点未设置，跳过倒计时系统初始化");
            return;
        }
        // 初始状态下隐藏倒计时节点
        this.countdownNode.active = false;
        // 重置倒计时状态
        this.isCountdownActive = false;
        this.lastCountdownEventId = "";
    };
    /**
     * 初始化回合数显示系统
     */
    GamePageController.prototype.initializeRoundDisplaySystem = function () {
        if (!this.roundDisplayNode) {
            console.warn("回合数显示节点未设置，跳过回合数显示系统初始化");
            return;
        }
        // 开局时隐藏节点
        this.roundDisplayNode.active = false;
        // 重置回合数
        this.currentRound = 1;
    };
    /**
     * 更新回合数显示
     * @param roundNumber 回合数
     */
    GamePageController.prototype.updateRoundDisplay = function (roundNumber) {
        if (!this.roundDisplayNode) {
            console.warn("回合数显示节点未设置");
            return;
        }
        // 更新当前回合数
        this.currentRound = roundNumber;
        // 显示节点（第一次显示时才激活）
        if (!this.roundDisplayNode.active) {
            this.roundDisplayNode.active = true;
        }
        // 查找子节点上的Label组件
        var labelComponent = this.roundDisplayNode.getComponentInChildren(cc.Label);
        if (!labelComponent) {
            console.warn("回合数显示节点的子节点中未找到Label组件");
            return;
        }
        // 优先使用插件的 LocalizedLabel
        try {
            // 优先在与 Label 同一节点上获取 LocalizedLabel
            var labelNode = labelComponent.node;
            var localized = labelNode.getComponent('LocalizedLabel');
            if (!localized) {
                // 若未挂载则在运行时添加（编辑器里可直接拖挂）
                localized = labelNode.addComponent('LocalizedLabel');
            }
            if (localized) {
                // 设置多语言 key 与参数
                localized.key = 'round_display';
                if (typeof localized.bindParam === 'function') {
                    localized.bindParam(roundNumber.toString());
                }
                else if (typeof localized.bindParamStrArray === 'function') {
                    localized.bindParamStrArray([roundNumber.toString()]);
                }
                // LocalizedLabel.refresh 会在 setter 或 bindParam 内部调用，不需要手动设置 string
                return;
            }
        }
        catch (e) {
            // 如果插件组件不可用，回退到内置简化版
            console.warn('LocalizedLabel 不可用，回退到内置多语言实现', e);
        }
        // 回退方案：使用简化版多语言函数
        var i18nText = this.getI18nText('round_display', [roundNumber.toString()]);
        labelComponent.string = i18nText;
    };
    /**
     * 处理倒计时开始（GameEventWaiting）
     */
    GamePageController.prototype.handleCountdownStart = function (_eventData) {
        // 等待阶段：禁用摇杆并显示倒计时动画
        if (this.joystickController) {
            this.joystickController.setInteractable(false);
        }
        // 清空待发送的输入帧，防止残留
        this.frameInputBuffer = [];
        // 重置本地移动状态
        this.isPlayerMoving = false;
        this.currentPlayerVelocity = cc.Vec2.ZERO;
        // 彻底取消本地玩家的任何平滑和解/目标位移，避免等待期间被推进
        if (this.myPlayerData) {
            var myId = this.myPlayerData.userId;
            this.reconciliationStates.delete(myId);
            this.playerMovingStates.set(myId, false);
            this.targetPositions.delete(myId);
            // 启动硬冻结（安全获取本地玩家当前位置）
            this.isLocalFrozen = true;
            if (this.myAnimalNode && cc.isValid(this.myAnimalNode)) {
                try {
                    var pos = this.myAnimalNode.getPosition ? this.myAnimalNode.getPosition() : { x: this.myAnimalNode.x, y: this.myAnimalNode.y };
                    var px = (pos && typeof pos.x === 'number') ? pos.x : 0;
                    var py = (pos && typeof pos.y === 'number') ? pos.y : 0;
                    this.frozenLocalPos = cc.v2(px, py);
                }
                catch (e) {
                    this.frozenLocalPos = cc.v2(0, 0);
                }
            }
            else {
                this.frozenLocalPos = cc.v2(0, 0);
            }
        }
        // 显示倒计时节点并播放动画（播放完成后自动隐藏）
        this.playCountdownAnimation();
    };
    /**
     * 处理倒计时结束（GameEventRoundWaiting）
     */
    GamePageController.prototype.handleCountdownEnd = function (eventData) {
        // 新逻辑：结束等待阶段，仅恢复摇杆交互；
        // 不再由此处控制倒计时节点的显示/隐藏，统一交给骨骼动画完成事件处理。
        // 但如果本地玩家已死亡，则保持摇杆禁用状态
        if (this.joystickController && !this.isLocalPlayerDead()) {
            this.joystickController.setInteractable(true);
            // 摇杆启用时隐藏提示动画
            this.hideTishiAnimation();
        }
        // 结束等待阶段
        this.isWaitingPhase = false;
        // 解除本地玩家的硬冻结状态
        this.isLocalFrozen = false;
        // 更新回合数显示（在回合等待状态时显示当前回合数）
        if (eventData && eventData.currentRound) {
            this.updateRoundDisplay(eventData.currentRound);
        }
        // 隐藏jingbao节点（在回合等待阶段隐藏难度升级动画）
        this.hideJingbaoAnimation();
    };
    /**
     * 播放倒计时动画
     */
    GamePageController.prototype.playCountdownAnimation = function () {
        if (!this.countdownNode) {
            console.error("❌ 倒计时节点未设置");
            return;
        }
        // 防止重复调用：如果已经在播放中，则忽略
        if (this.isCountdownActive) {
            console.warn("⚠️ 倒计时动画已在播放中，忽略重复调用");
            return;
        }
        // 确保节点可见在最前层
        this.countdownNode.active = true;
        this.countdownNode.zIndex = 9999;
        this.countdownNode.opacity = 255;
        // 从倒计时节点获取骨骼动画组件
        var skeletonComponent = this.countdownNode.getComponent(sp.Skeleton);
        if (!skeletonComponent) {
            console.error("❌ 倒计时节点上未找到骨骼动画组件");
            return;
        }
        // 立即设置为激活状态，防止重复调用
        this.isCountdownActive = true;
        // 检查父节点状态
        var parentNode = this.countdownNode.parent;
        while (parentNode) {
            if (!parentNode.active) {
                console.warn("\u26A0\uFE0F \u5012\u8BA1\u65F6\u8282\u70B9\u7684\u7236\u8282\u70B9 " + parentNode.name + " \u4E0D\u53EF\u89C1\uFF0C\u53EF\u80FD\u5F71\u54CD\u663E\u793A");
            }
            parentNode = parentNode.parent;
        }
        // 首先尝试直接播放，如果失败再使用重试机制
        if (skeletonComponent.skeletonData) {
            this.playCountdownAnimationDirect(skeletonComponent);
        }
        else {
            this.tryPlayCountdownAnimation(skeletonComponent, 0);
        }
    };
    /**
     * 直接播放倒计时动画（骨骼数据已准备好的情况）
     */
    GamePageController.prototype.playCountdownAnimationDirect = function (skeletonComponent) {
        var _this = this;
        try {
            // 播放动画
            skeletonComponent.setCompleteListener(null); // 清除旧的监听，避免重复回调
            // 检查动画是否存在
            try {
                var animationData = skeletonComponent.skeletonData;
                if (animationData && animationData.animations) {
                    var animations = animationData.animations;
                    var goAnimation = animations.find(function (anim) { return anim.name === "go"; });
                    if (goAnimation) {
                    }
                    else {
                        console.warn("\u26A0\uFE0F \u672A\u627E\u5230\"go\"\u52A8\u753B\uFF0C\u53EF\u80FD\u5BFC\u81F4\u7ACB\u5373\u5B8C\u6210");
                    }
                }
                else {
                    console.warn("\u26A0\uFE0F \u65E0\u6CD5\u83B7\u53D6\u52A8\u753B\u6570\u636E");
                }
            }
            catch (error) {
                console.warn("\u26A0\uFE0F \u68C0\u67E5\u52A8\u753B\u65F6\u51FA\u9519:", error);
            }
            skeletonComponent.setAnimation(0, "go", false);
            // 播放321go音效
            this.play321GoSoundEffect();
            // 检查动画时长，如果过短则使用备用方案
            var animationDuration = 0;
            try {
                var animationData = skeletonComponent.skeletonData;
                if (animationData && animationData.animations) {
                    var animations = animationData.animations;
                    var goAnimation = animations.find(function (anim) { return anim.name === "go"; });
                    if (goAnimation && goAnimation.duration) {
                        animationDuration = goAnimation.duration;
                    }
                }
            }
            catch (error) {
                console.warn("获取动画时长失败:", error);
            }
            // 验证节点状态
            // 动画播放结束后自动隐藏并重置标志
            if (animationDuration > 0 && animationDuration < 1.0) {
                // 如果动画时长小于1秒，使用人工延迟确保用户能看到
                this.scheduleOnce(function () {
                    if (_this.countdownNode) {
                        _this.countdownNode.active = false;
                    }
                    _this.isCountdownActive = false;
                    // 清除监听，避免下次重复
                    if (skeletonComponent) {
                        skeletonComponent.setCompleteListener(null);
                    }
                }, 2.0); // 固定显示2秒
            }
            else {
                // 动画时长正常，使用动画完成回调
                skeletonComponent.setCompleteListener(function () {
                    if (_this.countdownNode) {
                        _this.countdownNode.active = false;
                    }
                    _this.isCountdownActive = false;
                    // 清除监听，避免下次重复
                    if (skeletonComponent) {
                        skeletonComponent.setCompleteListener(null);
                    }
                });
            }
            // 多次验证节点状态
            this.scheduleOnce(function () {
            }, 0.05); // 50ms后验证
            this.scheduleOnce(function () {
            }, 0.1); // 100ms后验证
            this.scheduleOnce(function () {
                if (_this.countdownNode && _this.countdownNode.active) {
                }
                else {
                    console.error("\u274C \u5EF6\u8FDF\u9A8C\u8BC1: \u5012\u8BA1\u65F6\u8282\u70B9\u4E0D\u53EF\u89C1\uFF01\u53EF\u80FD\u88AB\u5176\u4ED6\u4EE3\u7801\u9690\u85CF\u4E86");
                    console.error("\u274C \u9A8C\u8BC1\u65F6\u72B6\u6001: isCountdownActive=" + _this.isCountdownActive + ", countdownNode\u5B58\u5728=" + !!_this.countdownNode);
                }
            }, 0.2); // 200ms后验证
        }
        catch (error) {
            console.error("❌ 直接播放倒计时动画失败:", error);
            this.tryPlayCountdownAnimation(skeletonComponent, 0);
        }
    };
    /**
     * 递增延迟重试播放倒计时动画
     */
    GamePageController.prototype.tryPlayCountdownAnimation = function (skeletonComponent, retryCount) {
        var _this = this;
        var maxRetries = 5;
        var delayTime = 0.1 + retryCount * 0.1; // 递增延迟：0.1s, 0.2s, 0.3s, 0.4s, 0.5s
        this.scheduleOnce(function () {
            try {
                // 检查动画是否存在
                if (!skeletonComponent || !skeletonComponent.skeletonData) {
                    console.warn("\u274C \u5012\u8BA1\u65F6\u52A8\u753B\u91CD\u8BD5" + (retryCount + 1) + "/" + maxRetries + "\uFF1A\u9AA8\u9ABC\u6570\u636E\u672A\u51C6\u5907\u597D");
                    if (retryCount < maxRetries - 1) {
                        _this.tryPlayCountdownAnimation(skeletonComponent, retryCount + 1);
                    }
                    else {
                        console.error("❌ 倒计时动画重试失败，尝试备用方案");
                        _this.tryPlayFallbackCountdownAnimation(skeletonComponent);
                    }
                    return;
                }
                // 骨骼数据准备好后，调用直接播放方法
                _this.playCountdownAnimationDirect(skeletonComponent);
            }
            catch (error) {
                console.error("\u274C \u5012\u8BA1\u65F6\u52A8\u753B\u91CD\u8BD5" + (retryCount + 1) + "/" + maxRetries + "\u5931\u8D25:", error);
                if (retryCount < maxRetries - 1) {
                    _this.tryPlayCountdownAnimation(skeletonComponent, retryCount + 1);
                }
                else {
                    console.error("❌ 倒计时动画重试失败，尝试备用方案");
                    _this.tryPlayFallbackCountdownAnimation(skeletonComponent);
                }
            }
        }, delayTime);
    };
    /**
     * 备用倒计时动画播放方案
     */
    GamePageController.prototype.tryPlayFallbackCountdownAnimation = function (skeletonComponent) {
        var _this = this;
        try {
            // 尝试播放常见的备用动画名称
            var fallbackAnimations = ["go", "default", "idle", "321go"];
            for (var _i = 0, fallbackAnimations_2 = fallbackAnimations; _i < fallbackAnimations_2.length; _i++) {
                var animName = fallbackAnimations_2[_i];
                try {
                    // 直接尝试播放动画，不进行复杂的检查
                    skeletonComponent.setCompleteListener(null);
                    skeletonComponent.setAnimation(0, animName, false);
                    this.isCountdownActive = true;
                    // 播放321go音效
                    this.play321GoSoundEffect();
                    // 设置完成监听
                    skeletonComponent.setCompleteListener(function () {
                        if (_this.countdownNode) {
                            _this.countdownNode.active = false;
                        }
                        _this.isCountdownActive = false;
                        if (skeletonComponent) {
                            skeletonComponent.setCompleteListener(null);
                        }
                    });
                    return;
                }
                catch (e) {
                    console.warn("\u5907\u7528\u52A8\u753B" + animName + "\u64AD\u653E\u5931\u8D25:", e);
                }
            }
            // 如果所有备用动画都失败，至少保证音效播放和节点隐藏
            console.error("❌ 所有倒计时动画都失败，只播放音效");
            this.play321GoSoundEffect();
            // 延迟隐藏节点（模拟动画时长）
            this.scheduleOnce(function () {
                if (_this.countdownNode) {
                    _this.countdownNode.active = false;
                }
                _this.isCountdownActive = false;
            }, 2.0); // 假设动画时长约2秒
        }
        catch (error) {
            console.error("❌ 备用倒计时动画方案也失败:", error);
            // 最后的保底方案：直接隐藏节点
            if (this.countdownNode) {
                this.countdownNode.active = false;
            }
            this.isCountdownActive = false;
        }
    };
    /**
     * 【核心】平滑移动更新 - 让其他玩家向目标位置平滑移动
     */
    GamePageController.prototype.updateSmoothMovement = function (dt) {
        var _this = this;
        this.allPlayerAnimals.forEach(function (animalNode, userId) {
            // 跳过已掉落或正在掉落的玩家（避免干扰掉落动画）
            if (_this.fallenPlayers.has(userId) || _this.fallingPlayers.has(userId)) {
                return;
            }
            // 检查是否是本地玩家
            var isLocalPlayer = (_this.myPlayerData && userId === _this.myPlayerData.userId);
            var isReconciling = _this.reconciliationStates.get(userId) || false;
            // 远端玩家若启用“渲染插值”，则不走目标追赶平滑，避免双写导致的抖动
            if (!isLocalPlayer && _this.useRenderInterpolationForRemotes) {
                return;
            }
            // 等待阶段时，彻底禁止本地玩家的任何位移（包括和解导致的平滑移动）
            if (isLocalPlayer && _this.isWaitingPhase) {
                _this.playerMovingStates.set(userId, false);
                _this.reconciliationStates.delete(userId);
                return;
            }
            // 本地玩家只在和解状态下才使用平滑移动
            if (isLocalPlayer && !isReconciling) {
                return;
            }
            var targetPosition = _this.targetPositions.get(userId);
            var isMoving = _this.playerMovingStates.get(userId);
            if (!targetPosition || !isMoving) {
                return;
            }
            var currentPosition = animalNode.getPosition();
            var currentVec2 = cc.v2(currentPosition.x, currentPosition.y);
            // 计算方向和距离（使用clone避免修改原向量）
            var direction = targetPosition.clone().sub(currentVec2);
            var distance = direction.mag();
            // 如果已经很接近目标位置，直接到达并停止移动
            if (distance < _this.positionThreshold) {
                animalNode.x = targetPosition.x;
                animalNode.y = targetPosition.y;
                _this.playerMovingStates.set(userId, false);
                _this.reconciliationStates.delete(userId);
                return;
            }
            // 计算移动速度上限（和解状态下提高，但限制最大追赶速度）
            var currentMoveSpeed = isReconciling ? _this.moveSpeed * 1.5 : _this.moveSpeed;
            if (isReconciling) {
                currentMoveSpeed = Math.min(currentMoveSpeed, _this.maxReconciliationSpeed);
            }
            // 指数平滑比例（FPS无关）：alpha = 1 - exp(-k*dt)
            var k = 10.0; // 收敛速率，手感参数
            var alpha = 1 - Math.exp(-k * Math.max(0, dt));
            // 期望按比例靠拢，但每帧位移不超过速度上限
            var desiredStep = distance * alpha;
            var maxStep = currentMoveSpeed * dt;
            var step = Math.min(desiredStep, maxStep);
            // 防止超调：如果这一步会超过剩余距离，直接到达目标
            if (step >= distance) {
                animalNode.x = targetPosition.x;
                animalNode.y = targetPosition.y;
                _this.playerMovingStates.set(userId, false);
                _this.reconciliationStates.delete(userId);
                if (isLocalPlayer) {
                }
            }
            else {
                // 平滑移动一小段距离（指数平滑 + 速度上限）
                var normalizedDirection = direction.normalize();
                var displacement = normalizedDirection.mul(step);
                animalNode.x = currentPosition.x + displacement.x;
                animalNode.y = currentPosition.y + displacement.y;
                // 更新玩家动画状态
                _this.updatePlayerAnimationState(userId, true);
                if (isLocalPlayer) {
                    var remain = distance - step;
                    // 可根据需要使用 remain 做额外处理
                }
            }
        });
    };
    /**
     * 精度控制工具函数 - 四舍五入保留6位小数（性能优化版）
     */
    GamePageController.prototype.roundToPrecision = function (value) {
        return Math.round(value * this.PRECISION_FACTOR) / this.PRECISION_FACTOR;
    };
    /**
     * 执行逻辑帧 - 简化版：不再进行前端位置计算
     */
    GamePageController.prototype.executeLogicFrame = function () {
        // 固定步长推进本地玩家预测与和解，确保与服务器10ms节奏一致
        if (!this.myAnimalNode || !cc.isValid(this.myAnimalNode) || !this.myPlayerData) {
            return;
        }
        // 🔒 如果本地玩家正在掉落，完全禁止逻辑帧更新
        if (this.fallingPlayers.has(this.myPlayerData.userId)) {
            return;
        }
        // 若不可交互/被阻止/已死亡，则不推进本地预测
        var canInteract = !!this.joystickController && this.joystickController.isInteractable() && !this.isInputBlocked && !this.isLocalPlayerDead();
        var isReconciling = this.reconciliationStates.get(this.myPlayerData.userId) || false;
        // 额外：即使无输入，也检测当前与其他玩家是否发生重叠碰撞
        // 若发生碰撞，则强制进入“跟随后端”模式与和解状态
        var currentPosForCollision = this.myAnimalNode.getPosition();
        var collidingNow = this.checkLocalCollision(currentPosForCollision.x, currentPosForCollision.y);
        if (collidingNow) {
            this.isCollisionFollowing = true;
            this.collisionReleaseFrames = 0;
            // 标记和解，等待后端权威位置推进
            this.reconciliationStates.set(this.myPlayerData.userId, true);
        }
        else {
            // 连续若干帧无碰撞则释放跟随模式
            if (this.isCollisionFollowing) {
                this.collisionReleaseFrames++;
                if (this.collisionReleaseFrames >= 3) {
                    this.isCollisionFollowing = false;
                    this.collisionReleaseFrames = 0;
                }
            }
        }
        // 碰撞跟随模式下，无论是否有输入，都跳过本地预测，直接按后端进行和解
        if (!this.isCollisionFollowing && canInteract && this.currentPlayerVelocity.mag() > 0.01) {
            var currentPos = this.myAnimalNode.getPosition();
            var compensatedVelocity = this.applyPredictionCompensation();
            var displacement = compensatedVelocity.mul(GamePageController_1.LOGIC_FRAME_DT);
            var predictedX = currentPos.x + displacement.x;
            var predictedY = currentPos.y + displacement.y;
            switch (this.collisionStrategy) {
                case 'aggressive':
                    this.handleAggressiveMovement(cc.v2(currentPos.x, currentPos.y), predictedX, predictedY, displacement);
                    break;
                case 'conservative':
                    this.handleConservativeMovement(cc.v2(currentPos.x, currentPos.y), predictedX, predictedY, displacement);
                    break;
                case 'hybrid':
                default:
                    this.handleHybridMovement(cc.v2(currentPos.x, currentPos.y), predictedX, predictedY, displacement);
                    break;
            }
            // 预测后在固定步中进行和解校正，减小误差
            if (isReconciling && this.lastServerPosition) {
                if (!cc.isValid(this.myAnimalNode))
                    return;
                this.applyOptimizedReconciliation(GamePageController_1.LOGIC_FRAME_DT);
            }
        }
        else {
            // 无输入或处于碰撞跟随模式：持续进行和解（如果需要）
            if ((isReconciling || this.isCollisionFollowing) && this.lastServerPosition) {
                this.applyOptimizedReconciliation(GamePageController_1.LOGIC_FRAME_DT, true);
            }
        }
    };
    /**
     * 定期检查所有玩家的待机动画状态
     */
    GamePageController.prototype.checkIdleAnimations = function () {
        var _this = this;
        var currentTime = Date.now();
        this.allPlayerData.forEach(function (playerData, userId) {
            var lastMovementTime = _this.lastMovementTimes.get(userId);
            var currentState = _this.currentAnimationStates.get(userId);
            if (!lastMovementTime || !currentState)
                return;
            var timeSinceLastMovement = currentTime - lastMovementTime;
            // 只有在静止状态下才检查待机动画
            if (currentState === _this.ANIMATION_STATES.STATIC &&
                timeSinceLastMovement >= _this.IDLE_TIMEOUT) {
                var animalType = _this.ANIMAL_TYPES[playerData.playerType];
                if (animalType) {
                    var idleAnimationName = _this.ANIMATION_STATES.IDLE + "/" + animalType;
                    _this.playAnimalAnimation(userId, idleAnimationName, false);
                    _this.currentAnimationStates.set(userId, _this.ANIMATION_STATES.IDLE);
                    // 待机动画播放完成后回到静止状态
                    _this.scheduleIdleAnimationComplete(userId, animalType);
                }
            }
        });
    };
    /**
     * 初始化待机动画检查定时器
     */
    GamePageController.prototype.initIdleAnimationTimer = function () {
        // 性能优化：延长待机动画检查间隔，减少CPU占用
        this.schedule(this.checkIdleAnimations, 2.0); // 从1秒改为2秒
    };
    /**
     * 清理待机动画检查定时器
     */
    GamePageController.prototype.clearIdleAnimationTimer = function () {
        this.unschedule(this.checkIdleAnimations);
    };
    /**
     * 更新调试信息到 debugInfoLabel（中文参数）
     */
    GamePageController.prototype.updateDebugInfo = function (dt) {
        if (!this.debugInfoLabel)
            return;
        // FPS 统计
        this.fpsAccumTime += dt;
        this.fpsFrameCount += 1;
        var needRefresh = false;
        if (this.fpsAccumTime >= 0.5) { // 每0.5秒刷新一次，避免频繁更新
            this.currentFps = Math.round(this.fpsFrameCount / this.fpsAccumTime);
            this.fpsAccumTime = 0;
            this.fpsFrameCount = 0;
            needRefresh = true;
        }
        // 若没有新的FPS也每1秒兜底刷新一次
        if (!needRefresh)
            return;
        // 网络抖动均值
        var avgJitter = this.jitterCount > 0 ? (this.jitterSum / this.jitterCount) : 0;
        // 帧接收间隔均值（ms）
        var avgFrameInterval = 0;
        if (this.frameReceiveIntervals && this.frameReceiveIntervals.length > 0) {
            var sum = this.frameReceiveIntervals.reduce(function (a, b) { return a + b; }, 0);
            avgFrameInterval = Math.round(sum / this.frameReceiveIntervals.length);
        }
        // 最近误差（本地与服务器位置差，仅本地玩家）
        var diffStr = "-";
        var diffVal = 0;
        if (this.myAnimalNode && this.lastServerPosition && cc.isValid(this.myAnimalNode)) {
            var p = this.myAnimalNode.getPosition();
            var dx = this.lastServerPosition.x - p.x;
            var dy = this.lastServerPosition.y - p.y;
            var d = Math.sqrt(dx * dx + dy * dy);
            diffVal = d;
            diffStr = d.toFixed(1);
        }
        // 最近10秒和解次数
        var now = Date.now();
        var recentRecon = this.reconciliationHistory.filter(function (t) { return now - t < 10000; }).length;
        // 更新网络状态图标（net/Network）
        this.updateNetworkIndicators(diffVal, now);
        // 碰撞与跟随状态
        var collisionStr = "-";
        if (this.myAnimalNode && cc.isValid(this.myAnimalNode)) {
            var p = this.myAnimalNode.getPosition();
            var collidingNow = this.checkLocalCollision(p.x, p.y);
            collisionStr = collidingNow ? '是' : '否';
        }
        var followStr = this.isCollisionFollowing ? '是' : '否';
        // 组装中文调试信息
        var lines = [];
        lines.push("FPS: " + this.currentFps);
        lines.push("\u7F51\u7EDC\u8D28\u91CF: " + this.networkQuality + "  \u5E73\u5747\u5EF6\u8FDF(ms): " + Math.round(this.averageLatency) + "  \u6296\u52A8(ms): " + Math.round(avgJitter) + "  \u4E22\u5305\u7387: " + (this.packetLossRate * 100).toFixed(1) + "%");
        lines.push("\u5E27\u63A5\u6536\u95F4\u9694\u5747\u503C(ms): " + avgFrameInterval + "  \u6E32\u67D3\u5EF6\u8FDF(ms): " + Math.round(this.adaptiveRenderDelay));
        lines.push("\u548C\u89E3\u9608\u503C: " + Math.round(this.adaptiveThreshold) + "  \u6700\u5927\u9608\u503C: " + Math.round(this.maxReconciliationThreshold) + "  \u9884\u6D4B\u7F6E\u4FE1\u5EA6: " + this.predictionConfidence.toFixed(2));
        lines.push("\u672C\u5730\u901F\u5EA6(px/s): " + this.moveSpeed + "  \u548C\u89E3\u8FFD\u8D76\u4E0A\u9650(px/s): " + this.maxReconciliationSpeed);
        lines.push("\u5F31\u7F51\u6A21\u5F0F: " + (this.weakNetworkMode ? '是' : '否') + "  \u5916\u63A8: " + (this.extrapolationEnabled ? '开' : '关'));
        lines.push("\u672C\u5730-\u670D\u52A1\u5668\u4F4D\u7F6E\u5DEE(px): " + diffStr);
        lines.push("\u6700\u8FD110\u79D2\u548C\u89E3\u6B21\u6570: " + recentRecon);
        lines.push("\u78B0\u649E\u4E2D: " + collisionStr + "  \u78B0\u649E\u8DDF\u968F: " + followStr);
        this.debugInfoLabel.string = lines.join('\n');
    };
    /**
     * 根据误差与和解频率更新网络状态显示，并结合networkQuality进行综合评估
     * good: 不回滚且网络质量良好
     * medium: 轻微且非连续回滚（误差<50 且近期有少量回滚，非连续）或网络质量一般
     * poor: 连续回滚（误差≥50 且连续超阈，或短时内和解频繁）或网络质量差
     *
     * 综合评估规则：
     * - networkQuality为'bad'时，good->poor, medium->poor
     * - networkQuality为'poor'时，good->medium
     */
    GamePageController.prototype.updateNetworkIndicators = function (diffVal, nowTs) {
        // 统计短窗内的和解次数，判断是否连续
        var last3sRecon = this.reconciliationHistory.filter(function (t) { return nowTs - t < 3000; }).length;
        var last10sRecon = this.reconciliationHistory.filter(function (t) { return nowTs - t < 10000; }).length;
        var nextStatus = 'good';
        // 连续回滚：误差≥50 且连续超过和解阈值帧数>=3，或3秒内和解>=3次
        if ((diffVal >= 50 && this.consecutiveOverThresholdFrames >= 3) || last3sRecon >= 3) {
            nextStatus = 'poor';
        }
        else if (last10sRecon > 0 && this.consecutiveOverThresholdFrames < 3 && diffVal < 50) {
            // 轻微、非连续：10秒内有回滚，但不连续，误差<50
            nextStatus = 'medium';
        }
        else {
            // 无回滚：10秒内没有和解记录
            nextStatus = last10sRecon === 0 ? 'good' : 'medium';
        }
        // 🎯 结合networkQuality进行综合评估
        // 如果networkQuality显示网络质量差，即使和解状态良好也要降级显示
        var originalStatus = nextStatus;
        if (this.networkQuality === 'bad' && nextStatus === 'good') {
            nextStatus = 'poor';
        }
        else if (this.networkQuality === 'poor' && nextStatus === 'good') {
            nextStatus = 'medium';
        }
        else if (this.networkQuality === 'bad' && nextStatus === 'medium') {
            nextStatus = 'poor';
        }
        if (nextStatus === this._netStatus) {
            return; // 状态未变化不重复加载
        }
        this._netStatus = nextStatus;
        // 更新 net 图标
        if (this.net) {
            var iconPath = this.NET_ICON_PATH[nextStatus];
            this.setNodeSprite(this.net, iconPath);
            this.net.active = true;
        }
        // 更新 Network 横幅（仅在 medium/poor 时显示）
        if (this.Network) {
            if (nextStatus === 'medium') {
                this.setNodeSprite(this.Network, this.NETWORK_BANNER_PATH.medium);
                this.Network.active = true;
            }
            else if (nextStatus === 'poor') {
                this.setNodeSprite(this.Network, this.NETWORK_BANNER_PATH.poor);
                this.Network.active = true;
            }
            else {
                this.Network.active = false;
            }
        }
    };
    // 安全设置节点图片（resources/ 下相对路径，不含前缀）
    GamePageController.prototype.setNodeSprite = function (node, resPath) {
        if (!node || !resPath)
            return;
        cc.resources.load(resPath, cc.SpriteFrame, function (err, spriteFrame) {
            if (err) {
                cc.warn("[NetIcon] \u8D44\u6E90\u52A0\u8F7D\u5931\u8D25: " + resPath, err);
                return;
            }
            if (!cc.isValid(node))
                return;
            var sp = node.getComponent(cc.Sprite) || node.addComponent(cc.Sprite);
            sp.spriteFrame = spriteFrame;
        });
    };
    /**
     * 收集帧输入数据到缓冲区 - 严格收集5帧数据
     */
    GamePageController.prototype.collectFrameInput = function () {
        // 若被要求暂停发送与采集，则直接返回，避免在重连初始化前产生无效帧
        if (this.isFrameInputStopped)
            return;
        if (!this.joystickController)
            return;
        // 如果本地玩家已死亡，不收集输入
        if (this.isLocalPlayerDead())
            return;
        var inputX = 0;
        var inputY = 0;
        // 仅当摇杆可交互时才采集输入，否则发送零输入
        if (this.joystickController.isInteractable()) {
            var inputData = this.joystickController.getInputData();
            if (inputData) {
                inputX = this.roundToPrecision(inputData.x);
                inputY = this.roundToPrecision(inputData.y);
            }
        }
        // 构建单帧输入数据
        var frameInput = {
            frameId: ++this.clientFrameId,
            x: inputX,
            y: inputY,
            dt: 0.01 // 固定dt为0.01（10ms）
        };
        // 累积帧数据
        this.frameInputBuffer.push(frameInput);
        // 当累积到5帧时，标记为可以发送
        if (this.frameInputBuffer.length >= 5) {
            // 保持缓冲区只有5帧
            if (this.frameInputBuffer.length > 5) {
                this.frameInputBuffer = this.frameInputBuffer.slice(-5);
            }
        }
    };
    /**
     * 检查并发送批量输入数据（50ms间隔，严格5帧）
     */
    GamePageController.prototype.checkAndSendBatchInput = function () {
        // 如果已停止发送FrameInput或摇杆不可交互（等待阶段），直接返回
        if (this.isFrameInputStopped || !this.joystickController || !this.joystickController.isInteractable()) {
            return;
        }
        // 如果本地玩家已死亡，不发送输入
        if (this.isLocalPlayerDead()) {
            return;
        }
        var currentTime = Date.now();
        // 检查是否到了发送时间且有足够的帧数据
        if (currentTime - this.lastBatchSendTime >= this.batchSendInterval) {
            // 严格要求必须有5帧数据才发送
            if (this.frameInputBuffer.length >= 5) {
                this.sendBatchInput();
                this.lastBatchSendTime = currentTime;
            }
            else {
                // 如果帧数不足5帧，需要补齐
                this.fillFramesToFive();
                if (this.frameInputBuffer.length === 5) {
                    this.sendBatchInput();
                    this.lastBatchSendTime = currentTime;
                }
            }
        }
    };
    /**
     * 补齐帧数到5帧
     */
    GamePageController.prototype.fillFramesToFive = function () {
        var targetFrameCount = 5;
        var currentFrameCount = this.frameInputBuffer.length;
        if (currentFrameCount >= targetFrameCount) {
            return; // 已经有足够的帧
        }
        // 获取最后一帧作为补齐的模板（如果没有帧则使用零输入）
        var lastFrame = currentFrameCount > 0 ?
            this.frameInputBuffer[currentFrameCount - 1] : {
            frameId: this.clientFrameId,
            x: 0,
            y: 0,
            dt: 0.01
        };
        // 补齐到5帧
        for (var i = currentFrameCount; i < targetFrameCount; i++) {
            var fillFrame = {
                frameId: ++this.clientFrameId,
                x: lastFrame.x,
                y: lastFrame.y,
                dt: 0.01 // 固定dt为0.01（10ms）
            };
            this.frameInputBuffer.push(fillFrame);
        }
    };
    GamePageController.prototype.sendBatchInput = function () {
        // 严格检查必须是5帧
        if (this.frameInputBuffer.length !== 5) {
            console.error("\u53D1\u9001\u5E27\u6570\u9519\u8BEF: \u671F\u671B5\u5E27\uFF0C\u5B9E\u9645" + this.frameInputBuffer.length + "\u5E27");
            return;
        }
        // 转换GameInputFrame为ClientInputFrame格式
        var clientInputs = this.frameInputBuffer.map(function (frame) { return ({
            frameId: frame.frameId,
            x: frame.x,
            y: frame.y
        }); });
        var batchData = {
            clientInputs: clientInputs
        };
        // 发送到服务器
        WebSocketManager_1.WebSocketManager.GetInstance().sendMsg(MessageId_1.MessageId.MsgTypeFrameInput, batchData);
        // 清空缓冲区
        this.frameInputBuffer = [];
    };
    GamePageController.prototype.shouldCollectInput = function (inputData) {
        // 简化逻辑：总是收集输入数据，让服务器处理去重
        return true;
    };
    /**
     * 处理游戏帧数据（从服务器接收）- 使用新的playerPositions更新位置
     */
    GamePageController.prototype.handleGameFrame = function (gameFrameData) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
        // 记录网络抖动数据
        this.recordNetworkJitter();
        // 更新确认的帧ID
        this.confirmedFrameId = gameFrameData.serverFrameId;
        // 断线重连期间不在这里恢复发送输入：
        // 要求调整为“仅在收到 EnterRoom 并完成重建后才恢复发送”。
        // 因此这里不再解除 isFrameInputStopped，由 handleReconnection() 完成恢复。
        // 检查游戏事件，特别是eventType: 7（玩家死亡事件）
        if (gameFrameData.gameEvents) {
            var myUserId = (_b = (_a = GlobalBean_1.GlobalBean.GetInstance().loginData) === null || _a === void 0 ? void 0 : _a.userInfo) === null || _b === void 0 ? void 0 : _b.userId;
            for (var _i = 0, _m = gameFrameData.gameEvents; _i < _m.length; _i++) {
                var gameEvent = _m[_i];
                // 统一提取事件体，便于兼容 {eventType, eventData} 与扁平结构
                var anyEvent = gameEvent;
                var evtType = anyEvent.eventType;
                var evtData = anyEvent.eventData || anyEvent;
                if (gameEvent.eventType === 7) {
                    // 同时播放对应玩家的死亡（掉落）动画（不影响方块(eventType:4)的动画播放）
                    var deathUserId = anyEvent.userId || (anyEvent.eventData && anyEvent.eventData.userId);
                    this.handlePlayerDeathEvent(gameEvent);
                    // 只有当死亡的是本地玩家时，才停止发送FrameInput
                    if (deathUserId && myUserId && deathUserId === myUserId) {
                        this.isFrameInputStopped = true;
                    }
                }
                // 处理玩家状态事件（死亡、断线、退出、重连）
                if (gameEvent.eventType >= 7 && gameEvent.eventType <= 10) {
                    // 从eventData中提取userId（兼容不同的数据结构）
                    var userId = gameEvent.userId || (anyEvent.eventData && anyEvent.eventData.userId);
                    // 创建标准化的事件数据
                    var standardizedEvent = {
                        eventType: gameEvent.eventType,
                        userId: userId,
                        currentRound: ((_c = anyEvent.eventData) === null || _c === void 0 ? void 0 : _c.currentRound) || 0,
                        countDown: ((_d = anyEvent.eventData) === null || _d === void 0 ? void 0 : _d.countDown) || 0,
                        randomSeed: ((_e = anyEvent.eventData) === null || _e === void 0 ? void 0 : _e.randomSeed) || 0,
                        message: ((_f = anyEvent.eventData) === null || _f === void 0 ? void 0 : _f.message) || "\u73A9\u5BB6\u72B6\u6001\u4E8B\u4EF6" + gameEvent.eventType,
                        pixelX: (_g = anyEvent.eventData) === null || _g === void 0 ? void 0 : _g.pixelX,
                        pixelY: (_h = anyEvent.eventData) === null || _h === void 0 ? void 0 : _h.pixelY,
                        deathReason: (_j = anyEvent.eventData) === null || _j === void 0 ? void 0 : _j.deathReason,
                        surviveTime: (_k = anyEvent.eventData) === null || _k === void 0 ? void 0 : _k.surviveTime,
                        isAlive: (_l = anyEvent.eventData) === null || _l === void 0 ? void 0 : _l.isAlive // 🎯 提取isAlive字段（eventType:10使用）
                    };
                    this.handleGameEvent(standardizedEvent);
                }
                // 直接在这里转发方块相关事件(3-5)给方块动画管理器，确保动画必定触发
                if (gameEvent.eventType === 3 || gameEvent.eventType === 4 || gameEvent.eventType === 5) {
                    this.forwardBlockEventFromFrame(gameEvent);
                    // 播放对应的音效
                    this.playBlockSoundEffect(gameEvent.eventType);
                }
                // 新增：处理等待开始/结束事件(1/2)以驱动倒计时与摇杆锁定
                if (evtType === 1 || evtType === 2) {
                    var convertedEvent = {
                        eventType: evtType,
                        currentRound: (evtData && evtData.currentRound) || 1,
                        countDown: (evtData && evtData.countDown) || 0,
                        randomSeed: (evtData && evtData.randomSeed) || 0,
                        message: "",
                        blocks: []
                    };
                    // 使用统一的附加逻辑处理，内部会调用 handleCountdownStart/End
                    this.handleAdditionalEventLogic(convertedEvent);
                }
                // 新增：处理GameEventShowcase事件(11)
                if (gameEvent.eventType === 11) {
                    var convertedEvent = {
                        eventType: 11,
                        currentRound: (evtData && evtData.currentRound) || 1,
                        countDown: (evtData && evtData.countDown) || 0,
                        randomSeed: (evtData && evtData.randomSeed) || 0,
                        message: "",
                        blocks: []
                    };
                    // 调用handleAdditionalEventLogic处理
                    this.handleAdditionalEventLogic(convertedEvent);
                }
                // 收到结束事件(6)：立即停止输入与摇杆，避免继续发送FrameInput
                if (gameEvent.eventType === 6) {
                    this.isFrameInputStopped = true;
                    this.isInputBlocked = true;
                    this.frameInputBuffer = [];
                    if (this.joystickController) {
                        this.joystickController.setInteractable(false);
                    }
                }
            }
        }
        // 使用后端返回的playerPositions更新玩家位置
        if (gameFrameData.playerPositions) {
            this.applyPlayerPositions(gameFrameData.playerPositions);
        }
        else {
            // 如果没有playerPositions，检查是否有旧格式的playerInputs
            var anyData = gameFrameData;
            if (anyData.playerInputs) {
                console.warn("收到旧格式的GameFrame数据，包含playerInputs字段。请更新后端接口以支持新的playerPositions格式。");
            }
            else {
                console.warn("GameFrame数据中没有playerPositions字段:", gameFrameData);
            }
        }
    };
    /**
     * 应用后端返回的玩家位置信息
     */
    GamePageController.prototype.applyPlayerPositions = function (playerPositions) {
        if (!playerPositions || !Array.isArray(playerPositions)) {
            console.warn("playerPositions 数据无效:", playerPositions);
            return;
        }
        var currentTime = Date.now();
        for (var _i = 0, playerPositions_1 = playerPositions; _i < playerPositions_1.length; _i++) {
            var playerPos = playerPositions_1[_i];
            if (!playerPos || !playerPos.userId || !playerPos.positions) {
                console.warn("玩家位置数据无效:", playerPos);
                continue;
            }
            var animalNode = this.allPlayerAnimals.get(playerPos.userId);
            if (!animalNode)
                continue;
            // 跳过已掉落或正在掉落的玩家，不更新其位置（避免干扰掉落动画）
            // 但是要确保方块动画不受影响，只跳过位置更新
            if (this.fallenPlayers.has(playerPos.userId) || this.fallingPlayers.has(playerPos.userId)) {
                // 已掉落或正在掉落的玩家不更新位置，但不影响方块动画的播放
                console.log("\uD83D\uDD12 \u4F4D\u7F6E\u9501\u5B9A - \u8DF3\u8FC7\u73A9\u5BB6" + playerPos.userId + "\u7684\u4F4D\u7F6E\u66F4\u65B0\uFF08\u6B63\u5728\u6389\u843D\u4E2D\uFF09");
                continue;
            }
            // 处理该玩家的所有位置数据
            if (Array.isArray(playerPos.positions)) {
                for (var _a = 0, _b = playerPos.positions; _a < _b.length; _a++) {
                    var posData = _b[_a];
                    if (posData && typeof posData.x === 'number' && typeof posData.y === 'number') {
                        // 直接使用后端返回的权威位置信息
                        this.applyServerPosition(animalNode, playerPos.userId, posData, currentTime);
                    }
                }
            }
        }
    };
    /**
     * 应用后端权威位置到玩家动物节点（平滑移动版本）
     */
    GamePageController.prototype.applyServerPosition = function (animalNode, userId, posData, timestamp) {
        var serverPosition = cc.v2(posData.x, posData.y);
        // 🔒 如果玩家正在掉落，不进行任何位置更新或和解操作
        if (this.fallingPlayers.has(userId)) {
            return;
        }
        // 检查是否是本地玩家（需要特殊处理）
        var isLocalPlayer = (this.myPlayerData && userId === this.myPlayerData.userId);
        if (isLocalPlayer) {
            // 本地玩家：只进行服务器和解检查，不改变当前位置（除非误差过大）
            // 记录最新的服务器位置，供和解期间的误差校正使用
            this.lastServerPosition = serverPosition.clone();
            // 记录本地与服务器的位置差，便于定位回滚误差来源
            if (this.myAnimalNode && cc.isValid(this.myAnimalNode)) {
                var lp = this.myAnimalNode.getPosition();
                var dx = serverPosition.x - lp.x;
                var dy = serverPosition.y - lp.y;
                var dist = Math.sqrt(dx * dx + dy * dy);
                var now = Date.now();
                if (dist > 1 && now - this.lastErrorLogTime > 200) {
                    this.lastErrorLogTime = now;
                }
            }
            this.handleLocalPlayerReconciliation(animalNode, serverPosition, userId);
        }
        else {
            // 其他玩家
            if (this.useRenderInterpolationForRemotes) {
                // 采用渲染插值：不再设置 targetPosition，避免与插值系统打架
                // 仅依赖逻辑状态历史进行插值渲染
            }
            else {
                // 采用目标追赶平滑策略
                if (this.weakNetworkMode) {
                    this.handleWeakNetworkPlayerPosition(userId, serverPosition);
                }
                else {
                    this.setTargetPosition(userId, serverPosition);
                }
            }
        }
        // 更新逻辑状态用于渲染插值（对于其他玩家）
        if (!isLocalPlayer) {
            var velocity = this.calculateVelocityFromPosition(userId, posData);
            this.updateLogicState(userId, serverPosition, velocity);
            // 更新动画状态
            var isMoving = velocity.mag() > 0.1;
            this.updatePlayerAnimationState(userId, isMoving);
        }
    };
    /**
     * 为其他玩家设置目标位置，启动平滑插值移动
     */
    GamePageController.prototype.setTargetPosition = function (userId, targetPosition) {
        this.targetPositions.set(userId, targetPosition.clone());
        this.playerMovingStates.set(userId, true);
    };
    /**
     * 弱网环境下处理其他玩家位置 - 防止瞬移
     */
    GamePageController.prototype.handleWeakNetworkPlayerPosition = function (userId, serverPosition) {
        var animalNode = this.allPlayerAnimals.get(userId);
        if (!animalNode)
            return;
        // 获取或初始化位置缓冲区
        var positionBuffer = this.playerPositionBuffers.get(userId);
        if (!positionBuffer) {
            positionBuffer = [];
            this.playerPositionBuffers.set(userId, positionBuffer);
        }
        // 添加新位置到缓冲区
        positionBuffer.push(serverPosition.clone());
        if (positionBuffer.length > this.positionBufferSize) {
            positionBuffer.shift();
        }
        var currentPosition = animalNode.getPosition();
        var distance = cc.Vec2.distance(cc.v2(currentPosition.x, currentPosition.y), serverPosition);
        // 根据距离选择不同策略
        if (distance > 100) {
            // 距离过大，可能是网络延迟导致的跳跃，使用外推预测
            this.applyExtrapolationForPlayer(userId, animalNode, positionBuffer);
        }
        else if (distance > 30) {
            // 中等距离，使用缓冲平滑
            this.applyBufferedSmoothingForPlayer(userId, animalNode, positionBuffer);
        }
        else {
            // 距离较小，正常插值
            this.setTargetPosition(userId, serverPosition);
        }
    };
    /**
     * 为玩家应用外推预测 - 解决大幅跳跃
     */
    GamePageController.prototype.applyExtrapolationForPlayer = function (userId, animalNode, positionBuffer) {
        if (positionBuffer.length < 2) {
            // 数据不足，直接使用最新位置
            var latestPos = positionBuffer[positionBuffer.length - 1];
            this.setTargetPosition(userId, latestPos);
            return;
        }
        // 计算移动趋势
        var latest = positionBuffer[positionBuffer.length - 1];
        var previous = positionBuffer[positionBuffer.length - 2];
        var velocity = this.getVec2FromPool(latest.x - previous.x, latest.y - previous.y);
        // 基于趋势预测下一个位置
        var predictedPosition = this.getVec2FromPool(latest.x + velocity.x * 0.5, // 预测半步
        latest.y + velocity.y * 0.5);
        // 使用预测位置作为目标
        this.setTargetPosition(userId, predictedPosition);
        // 返回向量到对象池
        this.returnVec2ToPool(velocity);
        this.returnVec2ToPool(predictedPosition);
    };
    /**
     * 为玩家应用缓冲平滑 - 减少抖动
     */
    GamePageController.prototype.applyBufferedSmoothingForPlayer = function (userId, animalNode, positionBuffer) {
        if (positionBuffer.length < 2) {
            var latestPos = positionBuffer[positionBuffer.length - 1];
            this.setTargetPosition(userId, latestPos);
            return;
        }
        // 计算缓冲区中位置的平均值
        var avgX = 0, avgY = 0;
        for (var _i = 0, positionBuffer_1 = positionBuffer; _i < positionBuffer_1.length; _i++) {
            var pos = positionBuffer_1[_i];
            avgX += pos.x;
            avgY += pos.y;
        }
        avgX /= positionBuffer.length;
        avgY /= positionBuffer.length;
        var smoothedPosition = this.getVec2FromPool(avgX, avgY);
        this.setTargetPosition(userId, smoothedPosition);
        this.returnVec2ToPool(smoothedPosition);
    };
    /**
     * 处理本地玩家的服务器和解 - 优化版
     */
    GamePageController.prototype.handleLocalPlayerReconciliation = function (animalNode, serverPosition, userId) {
        var currentPosition = animalNode.getPosition();
        var distance = cc.Vec2.distance(cc.v2(currentPosition.x, currentPosition.y), serverPosition);
        var currentTime = Date.now();
        // 动态调整和解阈值
        this.updateAdaptiveThreshold();
        // 检查是否已经在和解状态
        var isAlreadyReconciling = this.reconciliationStates.get(userId) || false;
        // 防止频繁和解：最小间隔1200ms，进一步减少回滚频率
        var timeSinceLastReconciliation = currentTime - this.lastReconciliationTime;
        var minReconciliationInterval = 1200;
        // 连续超过阈值的计数（降低偶发尖峰触发回滚）
        if (distance > this.adaptiveThreshold) {
            this.consecutiveOverThresholdFrames++;
        }
        else {
            this.consecutiveOverThresholdFrames = 0;
        }
        // 检查是否过于频繁和解，如果是则暂时禁用
        var veryRecentReconciliations = this.reconciliationHistory.filter(function (time) {
            return currentTime - time < 3000;
        } // 3秒内
        );
        // 在“碰撞跟随模式”下，放宽/跳过触发门槛，立即进入和解，严格跟随后端
        if (this.isCollisionFollowing && !isAlreadyReconciling) {
            this.startSmoothReconciliation(userId, serverPosition);
            this.lastReconciliationTime = currentTime;
        }
        else if (distance > this.adaptiveThreshold && !isAlreadyReconciling &&
            timeSinceLastReconciliation > minReconciliationInterval &&
            veryRecentReconciliations.length < 3 &&
            this.consecutiveOverThresholdFrames >= 3) { // 连续3帧超过阈值才触发
            // 检查是否频繁和解，如果是则提高阈值
            this.recordReconciliation();
            // 启动平滑和解过程
            this.startSmoothReconciliation(userId, serverPosition);
            this.lastReconciliationTime = currentTime;
        }
        else if (distance <= 6 && isAlreadyReconciling) {
            // 更严格的停止条件，确保精确对齐
            this.reconciliationStates.delete(userId);
            this.playerMovingStates.set(userId, false);
            // 若已离开碰撞状态，也结束碰撞跟随模式
            if (!this.checkLocalCollision(currentPosition.x, currentPosition.y)) {
                this.isCollisionFollowing = false;
                this.collisionReleaseFrames = 0;
            }
        }
        // 更新预测置信度和网络延迟补偿
        this.updatePredictionConfidence(distance);
        this.updateNetworkDelayCompensation();
    };
    /**
     * 启动平滑服务器和解过程
     */
    GamePageController.prototype.startSmoothReconciliation = function (userId, serverPosition) {
        // 对于本地玩家，设置目标位置进行平滑和解
        this.targetPositions.set(userId, serverPosition.clone());
        this.playerMovingStates.set(userId, true);
        // 标记为和解状态，使用更高的移动速度
        this.reconciliationStates.set(userId, true);
        var node = this.allPlayerAnimals.get(userId);
        if (node) {
            var p = node.getPosition();
        }
    };
    /**
     * 动态调整和解阈值 - 激进提升版本
     */
    GamePageController.prototype.updateAdaptiveThreshold = function () {
        var recentReconciliations = this.reconciliationHistory.filter(function (time) {
            return Date.now() - time < 10000;
        } // 扩展到10秒内
        );
        // 根据最近的和解频率激进调整阈值
        if (recentReconciliations.length > 3) {
            // 频繁和解时大幅提高阈值
            this.adaptiveThreshold = Math.min(this.maxReconciliationThreshold, this.adaptiveThreshold + 5);
        }
        else if (recentReconciliations.length > 1) {
            // 中等频率时适度提高阈值
            this.adaptiveThreshold = Math.min(this.maxReconciliationThreshold, this.adaptiveThreshold + 2);
        }
        else if (recentReconciliations.length === 0) {
            // 长时间无和解时才缓慢降低阈值（最低不低于20）
            this.adaptiveThreshold = Math.max(20, this.adaptiveThreshold - 1);
        }
    };
    /**
     * 更新预测置信度
     */
    GamePageController.prototype.updatePredictionConfidence = function (distance) {
        // 根据与服务器位置的偏差调整预测置信度
        if (distance < 5) {
            this.predictionConfidence = Math.min(1.0, this.predictionConfidence + 0.02);
        }
        else if (distance > 20) {
            this.predictionConfidence = Math.max(0.3, this.predictionConfidence - 0.05);
        }
    };
    /**
     * 更新网络延迟补偿
     */
    GamePageController.prototype.updateNetworkDelayCompensation = function () {
        // 基于网络抖动历史计算延迟补偿
        if (this.jitterCount > 3) {
            var avgJitter = this.jitterSum / this.jitterCount;
            this.networkDelayCompensation = Math.min(50, avgJitter * 0.5);
        }
    };
    /**
     * 应用预测补偿 - 智能误差控制版本
     */
    GamePageController.prototype.applyPredictionCompensation = function () {
        var baseVelocity = this.currentPlayerVelocity;
        // 【新增】基于当前误差动态调整预测强度
        var predictionFactor = 1.0;
        // 如果当前与服务器位置差距较大，减少预测补偿，避免误差进一步累积
        if (this.lastServerPosition && this.myAnimalNode && cc.isValid(this.myAnimalNode)) {
            var currentPos = this.myAnimalNode.getPosition();
            var distance = cc.Vec2.distance(cc.v2(currentPos.x, currentPos.y), this.lastServerPosition);
            // 根据距离动态调整预测因子
            if (distance > 20) {
                predictionFactor = 0.7; // 大误差时减少预测
            }
            else if (distance > 10) {
                predictionFactor = 0.85; // 中等误差时适度减少
            }
            else if (distance > 5) {
                predictionFactor = 0.95; // 小误差时轻微减少
            }
        }
        // 在弱网模式下，适度增强本地预测以减少延迟感（但受误差控制限制）
        if (this.weakNetworkMode && this.extrapolationEnabled) {
            // 根据网络质量调整预测强度，但不超过误差控制的限制
            var networkFactor = 1.0;
            switch (this.networkQuality) {
                case 'bad':
                    networkFactor = 1.2; // 降低从1.3到1.2
                    break;
                case 'poor':
                    networkFactor = 1.1; // 降低从1.15到1.1
                    break;
                default:
                    networkFactor = 1.0;
            }
            // 取两个因子的较小值，优先控制误差
            predictionFactor = Math.min(predictionFactor, networkFactor);
        }
        return this.getVec2FromPool(baseVelocity.x * predictionFactor, baseVelocity.y * predictionFactor);
    };
    /**
     * 根据位置变化计算速度（用于动画状态判断）
     */
    GamePageController.prototype.calculateVelocityFromPosition = function (userId, currentPos) {
        var lastState = this.getLatestLogicState(userId);
        if (!lastState) {
            return cc.Vec2.ZERO;
        }
        var deltaX = currentPos.x - lastState.position.x;
        var deltaY = currentPos.y - lastState.position.y;
        // 计算速度（像素/秒）
        var velocity = cc.v2(deltaX / this.fixedDeltaTime, deltaY / this.fixedDeltaTime);
        return velocity;
    };
    /**
     * 更新动物朝向（根据移动方向旋转）- 添加精度控制
     */
    GamePageController.prototype.updateAnimalRotation = function (animalNode, velocity, dt) {
        // 只有在有移动时才更新朝向
        if (velocity.mag() < 0.1)
            return;
        // 【关键】应用精度控制到速度分量
        var preciseVelX = this.roundToPrecision(velocity.x);
        var preciseVelY = this.roundToPrecision(velocity.y);
        // 计算目标角度（弧度）
        var targetAngle = Math.atan2(preciseVelY, preciseVelX);
        // 转换为度数，并调整为动物预制体的默认朝向（向上）
        // 预制体默认向上，所以需要减去90度来校正
        var targetDegree = this.roundToPrecision((targetAngle * (180 / Math.PI)) - 90);
        // 获取当前角度并应用精度控制
        var currentDegree = this.roundToPrecision(animalNode.angle);
        // 计算角度差值（处理360度跨越）
        var angleDiff = targetDegree - currentDegree;
        // 将角度差值规范化到[-180, 180]范围
        while (angleDiff > 180)
            angleDiff -= 360;
        while (angleDiff < -180)
            angleDiff += 360;
        // 平滑旋转（避免突然跳跃）
        // rotationSmoothSpeed 视为“度/秒”，按真实 dt 推进，去除60FPS基准硬编码
        var rotationSpeed = this.rotationSmoothSpeed * dt;
        var maxRotationThisFrame = this.roundToPrecision(rotationSpeed);
        if (Math.abs(angleDiff) > maxRotationThisFrame) {
            // 需要分多帧旋转
            var rotationDirection = angleDiff > 0 ? 1 : -1;
            var newAngle = this.roundToPrecision(currentDegree + maxRotationThisFrame * rotationDirection);
            animalNode.angle = newAngle;
        }
        else {
            // 可以一次到位
            animalNode.angle = targetDegree;
        }
        // 记录当前移动方向（应用精度控制）
        var normalizedVel = cc.v2(preciseVelX, preciseVelY).normalize();
        this.lastMovementDirection = cc.v2(this.roundToPrecision(normalizedVel.x), this.roundToPrecision(normalizedVel.y));
    };
    /**
     * 根据动物节点获取玩家ID
     */
    GamePageController.prototype.getPlayerIdByAnimalNode = function (animalNode) {
        // 使用传统的forEach来避免迭代器兼容性问题
        var foundUserId = null;
        this.allPlayerAnimals.forEach(function (node, userId) {
            if (node === animalNode) {
                foundUserId = userId;
            }
        });
        return foundUserId;
    };
    /**
     * 获取玩家最新的逻辑状态
     */
    GamePageController.prototype.getLatestLogicState = function (userId) {
        var states = this.logicStates.get(userId);
        return states && states.length > 0 ? states[states.length - 1] : null;
    };
    /**
     * 更新玩家的逻辑状态
     */
    GamePageController.prototype.updateLogicState = function (userId, position, velocity) {
        if (!this.logicStates.has(userId)) {
            this.logicStates.set(userId, []);
        }
        var states = this.logicStates.get(userId);
        var newState = {
            position: position.clone(),
            velocity: velocity.clone(),
            timestamp: Date.now(),
            logicFrame: this.currentLogicFrame
        };
        states.push(newState);
        // 限制历史状态数量
        if (states.length > this.maxLogicStateHistory) {
            states.shift();
        }
    };
    GamePageController.prototype.updateRenderInterpolation = function (dt) {
        var _this = this;
        var currentTime = Date.now();
        // 帧率自适应机制
        this.updateAdaptiveFrameRate(currentTime);
        // 性能优化：使用自适应渲染间隔
        if (currentTime - this.lastRenderTime < this.adaptiveRenderInterval) {
            return;
        }
        this.lastRenderTime = currentTime;
        // 计算渲染时间点 - 总是比当前时间早一个缓冲时间
        var renderTime = currentTime - this.adaptiveRenderDelay;
        this.allPlayerAnimals.forEach(function (animalNode, userId) {
            // 如果启用远端插值，仅对远端玩家生效
            var isLocalPlayer = (_this.myPlayerData && userId === _this.myPlayerData.userId);
            if (isLocalPlayer || !_this.useRenderInterpolationForRemotes) {
                return;
            }
            var logicStates = _this.logicStates.get(userId);
            if (!logicStates || logicStates.length === 0)
                return;
            // 跳过已掉落或正在掉落的玩家，不更新其位置（避免干扰掉落动画）
            if (_this.fallenPlayers.has(userId) || _this.fallingPlayers.has(userId)) {
                return;
            }
            // 获取当前渲染状态
            var renderState = _this.renderStates.get(userId);
            if (!renderState) {
                // 初始化渲染状态
                var latestLogic = logicStates[logicStates.length - 1];
                renderState = {
                    position: latestLogic.position.clone(),
                    velocity: latestLogic.velocity.clone(),
                    lastLogicFrame: latestLogic.logicFrame
                };
                _this.renderStates.set(userId, renderState);
            }
            // 执行带缓冲的渲染插值（使用本帧渲染dt确保旋转速度与本地一致）
            _this.performBufferedRenderInterpolation(animalNode, userId, renderState, logicStates, renderTime, dt);
        });
        // 更新自适应渲染延迟
        this.updateAdaptiveRenderDelay();
    };
    /**
     * 帧率自适应机制 - 根据设备性能动态调整渲染间隔
     */
    GamePageController.prototype.updateAdaptiveFrameRate = function (currentTime) {
        if (this.lastFrameTime > 0) {
            var frameTime = currentTime - this.lastFrameTime;
            this.frameTimeHistory.push(frameTime);
            // 限制历史记录大小
            if (this.frameTimeHistory.length > this.maxFrameTimeHistory) {
                this.frameTimeHistory.shift();
            }
            // 计算平均帧时间
            if (this.frameTimeHistory.length >= 10) {
                var avgFrameTime = this.frameTimeHistory.reduce(function (sum, time) { return sum + time; }, 0) / this.frameTimeHistory.length;
                this.lastAvgFrameTime = avgFrameTime;
                // 根据平均帧时间调整渲染间隔
                if (avgFrameTime > 20) { // 低于50FPS
                    this.adaptiveRenderInterval = Math.min(16, this.adaptiveRenderInterval + 1);
                }
                else if (avgFrameTime < 12) { // 高于83FPS
                    this.adaptiveRenderInterval = Math.max(6, this.adaptiveRenderInterval - 1);
                }
            }
        }
        this.lastFrameTime = currentTime;
    };
    /**
     * 执行带缓冲的渲染插值计算 - 抖动缓冲的核心实现
     */
    GamePageController.prototype.performBufferedRenderInterpolation = function (animalNode, userId, renderState, logicStates, renderTime, dtRender) {
        var currentTime = Date.now();
        // 找到适合插值的两个逻辑状态
        var prevState = null;
        var nextState = null;
        // 记录找到区间时的索引，便于进行Catmull-Rom插值
        var prevIndex = -1;
        var nextIndex = -1;
        // 寻找渲染时间点应该在哪两个逻辑帧之间
        for (var i = 0; i < logicStates.length - 1; i++) {
            var state1 = logicStates[i];
            var state2 = logicStates[i + 1];
            if (state1.timestamp <= renderTime && state2.timestamp >= renderTime) {
                prevState = state1;
                nextState = state2;
                prevIndex = i;
                nextIndex = i + 1;
                break;
            }
        }
        // 如果没找到合适的区间，尝试使用最佳可用状态
        if (!prevState || !nextState) {
            var bestState = this.findBestStateForRenderTime(logicStates, renderTime);
            if (bestState) {
                animalNode.x = bestState.position.x;
                animalNode.y = bestState.position.y;
                // 更新渲染状态
                renderState.position = bestState.position.clone();
                renderState.velocity = bestState.velocity.clone();
                // 更新朝向
                if (bestState.velocity.mag() > 0.01) {
                    this.updateAnimalRotation(animalNode, bestState.velocity, dtRender);
                }
            }
            return;
        }
        // 计算插值进度
        var timeDiff = nextState.timestamp - prevState.timestamp;
        var progress = timeDiff > 0 ? (renderTime - prevState.timestamp) / timeDiff : 0;
        var clampedProgress = Math.max(0, Math.min(1, progress));
        // 检查插值缓存
        var cacheKey = userId + "_" + prevState.timestamp + "_" + nextState.timestamp;
        var cachedResult = this.interpolationCache.get(cacheKey);
        if (cachedResult && (currentTime - cachedResult.timestamp) < this.cacheValidDuration) {
            // 使用缓存结果
            animalNode.x = cachedResult.position.x;
            animalNode.y = cachedResult.position.y;
            // 更新朝向（如果有移动）
            if (cachedResult.velocity.mag() > 0.01) {
                this.updateAnimalRotation(animalNode, cachedResult.velocity, dtRender);
            }
            // 更新渲染状态
            renderState.position.x = cachedResult.position.x;
            renderState.position.y = cachedResult.position.y;
            renderState.velocity = cachedResult.velocity;
            return;
        }
        // 性能优化：减少向量创建和计算
        // 位置插值：优先使用Catmull-Rom（四点）以在低FPS时获得更平滑曲线，否则使用线性
        var targetX;
        var targetY;
        if (prevIndex > 0 && nextIndex < logicStates.length - 1) {
            var p0 = logicStates[prevIndex - 1].position;
            var p1 = prevState.position;
            var p2 = nextState.position;
            var p3 = logicStates[nextIndex + 1].position;
            targetX = this.catmullRom(p0.x, p1.x, p2.x, p3.x, clampedProgress);
            targetY = this.catmullRom(p0.y, p1.y, p2.y, p3.y, clampedProgress);
        }
        else {
            targetX = prevState.position.x + (nextState.position.x - prevState.position.x) * clampedProgress;
            targetY = prevState.position.y + (nextState.position.y - prevState.position.y) * clampedProgress;
        }
        // 低FPS下的额外混合：将上帧渲染位置与目标位置做一次平滑，降低单帧跳变
        var blendAlpha = 1.0;
        if (this.lastAvgFrameTime > 28) { // ~35FPS或更低
            blendAlpha = 0.6;
        }
        else if (this.lastAvgFrameTime > 20) { // ~50FPS附近
            blendAlpha = 0.85;
        }
        var prevRenderX = renderState.position.x;
        var prevRenderY = renderState.position.y;
        var blendedX = this.roundToPrecision(prevRenderX + (targetX - prevRenderX) * blendAlpha);
        var blendedY = this.roundToPrecision(prevRenderY + (targetY - prevRenderY) * blendAlpha);
        animalNode.x = blendedX;
        animalNode.y = blendedY;
        // 速度插值（性能优化：只在需要时计算）
        var velX = this.roundToPrecision(prevState.velocity.x + (nextState.velocity.x - prevState.velocity.x) * clampedProgress);
        var velY = this.roundToPrecision(prevState.velocity.y + (nextState.velocity.y - prevState.velocity.y) * clampedProgress);
        var velocityMagnitude = Math.sqrt(velX * velX + velY * velY);
        // 从对象池获取速度向量
        var interpolatedVelocity = velocityMagnitude > 0.01 ? this.getVec2FromPool(velX, velY) : this.getVec2FromPool(0, 0);
        // 更新朝向（如果有移动）
        if (velocityMagnitude > 0.01) {
            this.updateAnimalRotation(animalNode, interpolatedVelocity, dtRender);
        }
        // 缓存计算结果（缓存最终用于渲染的混合位置）
        var cachedPos = this.getVec2FromPool(blendedX, blendedY);
        var cachedVel = this.getVec2FromPool(velX, velY);
        this.interpolationCache.set(cacheKey, {
            position: cachedPos,
            velocity: cachedVel,
            timestamp: currentTime
        });
        // 限制缓存大小
        if (this.interpolationCache.size > 100) {
            var oldestKey = this.interpolationCache.keys().next().value;
            var oldResult = this.interpolationCache.get(oldestKey);
            if (oldResult) {
                this.returnVec2ToPool(oldResult.position);
                this.returnVec2ToPool(oldResult.velocity);
            }
            this.interpolationCache.delete(oldestKey);
        }
        // 更新渲染状态（性能优化：重用已计算的位置向量）
        renderState.position.x = blendedX;
        renderState.position.y = blendedY;
        // 返回旧的速度向量到对象池
        if (renderState.velocity) {
            this.returnVec2ToPool(renderState.velocity);
        }
        renderState.velocity = interpolatedVelocity;
    };
    // Catmull-Rom 样条插值（标量版本）
    GamePageController.prototype.catmullRom = function (p0, p1, p2, p3, t) {
        var t2 = t * t;
        var t3 = t2 * t;
        return 0.5 * ((2 * p1) +
            (-p0 + p2) * t +
            (2 * p0 - 5 * p1 + 4 * p2 - p3) * t2 +
            (-p0 + 3 * p1 - 3 * p2 + p3) * t3);
    };
    /**
     * 更新自适应渲染延迟 - 优化版网络抖动算法
     */
    GamePageController.prototype.updateAdaptiveRenderDelay = function () {
        if (this.jitterCount < 3) {
            // 数据不足，使用默认延迟
            return;
        }
        // 使用平均值而非排序，提升性能
        var avgJitter = this.jitterSum / this.jitterCount;
        // 计算新的自适应延迟（简化算法）
        var baseDelay = this.renderDelay;
        var adaptiveDelay = Math.max(baseDelay, Math.min(avgJitter * 1.5, this.jitterBufferSize));
        // 平滑调整，避免突然变化
        var smoothingFactor = 0.15; // 稍微提高响应速度
        this.adaptiveRenderDelay = this.adaptiveRenderDelay * (1 - smoothingFactor) + adaptiveDelay * smoothingFactor;
        // 确保延迟在合理范围内
        this.adaptiveRenderDelay = Math.max(40, Math.min(this.adaptiveRenderDelay, this.jitterBufferSize));
    };
    /**
     * 记录网络抖动数据 - 弱网优化版
     */
    GamePageController.prototype.recordNetworkJitter = function () {
        var currentTime = Date.now();
        // 更新包统计
        this.totalPackets++;
        if (this.lastFrameReceiveTime > 0) {
            var interval = currentTime - this.lastFrameReceiveTime;
            // 🎯 开局前5帧忽略异常间隔，避免初始化时的大间隔影响网络质量判断
            var isEarlyFrame = this.totalPackets <= 5;
            // 检测丢包（间隔过长）：放宽为 > 4 倍发送间隔，避免把合并/黏包误判为丢包
            // 开局前5帧使用更宽松的丢包检测（10倍间隔）
            var missThreshold = isEarlyFrame ? this.batchSendInterval * 10 : this.batchSendInterval * 4;
            var isMiss = interval > missThreshold ? 1 : 0;
            this.packetWindow.push(isMiss);
            if (this.packetWindow.length > this.packetWindowSize) {
                this.packetWindow.shift();
            }
            // 记录延迟 - 开局前5帧限制最大延迟，避免初始化延迟影响
            var recordedInterval = isEarlyFrame ? Math.min(interval, this.batchSendInterval * 3) : interval;
            this.latencyHistory.push(recordedInterval);
            if (this.latencyHistory.length > 20) {
                this.latencyHistory.shift();
            }
            // 计算平均延迟
            this.averageLatency = this.latencyHistory.reduce(function (sum, lat) { return sum + lat; }, 0) / this.latencyHistory.length;
            // 使用固定窗口计算丢包率，避免长期累积导致"黏住坏"
            var windowSum = this.packetWindow.reduce(function (sum, v) { return sum + v; }, 0);
            this.packetLossRate = this.packetWindow.length > 0 ? windowSum / this.packetWindow.length : 0;
            // 计算抖动（与期望的50ms间隔的差异）- 开局前5帧限制最大抖动
            var expectedInterval = this.batchSendInterval;
            var rawJitter = Math.abs(interval - expectedInterval);
            var jitter = isEarlyFrame ? Math.min(rawJitter, expectedInterval * 2) : rawJitter;
            // 使用滑动平均，避免数组操作
            this.jitterSum += jitter;
            this.jitterCount++;
            // 限制历史记录数量，使用滑动窗口
            if (this.jitterCount > this.maxJitterHistorySize) {
                // 重置计数器，保持最近的平均值
                this.jitterSum = this.jitterSum * 0.8; // 保留80%的历史影响
                this.jitterCount = Math.floor(this.jitterCount * 0.8);
            }
            // 保留少量历史记录用于其他计算
            this.networkJitterHistory.push(jitter);
            if (this.networkJitterHistory.length > 5) {
                this.networkJitterHistory.shift();
            }
        }
        this.lastFrameReceiveTime = currentTime;
        // 评估网络质量并调整策略
        this.evaluateNetworkQuality();
    };
    /**
     * 评估网络质量并启用相应策略
     */
    GamePageController.prototype.evaluateNetworkQuality = function () {
        var avgJitter = this.jitterCount > 0 ? this.jitterSum / this.jitterCount : 0;
        // 🎯 开局前10帧使用更宽松的网络质量评估，避免初始化影响
        var isEarlyGame = this.totalPackets <= 10;
        if (isEarlyGame) {
            // 开局阶段：使用更宽松的阈值，避免初始化时的异常数据影响判断
            if (this.packetLossRate > 0.3 || this.averageLatency > 500 || avgJitter > 300) {
                this.networkQuality = 'bad';
            }
            else if (this.packetLossRate > 0.2 || this.averageLatency > 300 || avgJitter > 200) {
                this.networkQuality = 'poor';
            }
            else {
                this.networkQuality = 'good';
            }
        }
        else {
            // 正常游戏阶段：使用标准阈值
            if (this.packetLossRate > 0.15 || this.averageLatency > 250 || avgJitter > 150) {
                this.networkQuality = 'bad';
            }
            else if (this.packetLossRate > 0.08 || this.averageLatency > 150 || avgJitter > 80) {
                this.networkQuality = 'poor';
            }
            else {
                this.networkQuality = 'good';
            }
        }
        // 根据网络质量调整策略
        this.adjustStrategiesForNetworkQuality();
    };
    /**
     * 根据网络质量调整策略
     */
    GamePageController.prototype.adjustStrategiesForNetworkQuality = function () {
        switch (this.networkQuality) {
            case 'bad':
                this.weakNetworkMode = true;
                this.extrapolationEnabled = true;
                this.adaptiveThreshold = 95; // 更高的和解阈值，降低频繁回滚
                this.reconciliationSmoothness = 0.03; // 更平滑的校正
                this.positionBufferSize = 8; // 增大缓冲区
                break;
            case 'poor':
                this.weakNetworkMode = true;
                this.extrapolationEnabled = true;
                this.adaptiveThreshold = 60; // 适度提高阈值
                this.reconciliationSmoothness = 0.04;
                this.positionBufferSize = 6;
                break;
            case 'good':
                this.weakNetworkMode = false;
                this.extrapolationEnabled = false;
                this.adaptiveThreshold = 20; // 正常网络下使用阈值20
                this.reconciliationSmoothness = 0.05;
                this.positionBufferSize = 5;
                break;
        }
    };
    /**
     * 如果需要，初始化玩家的逻辑状态
     */
    GamePageController.prototype.initializeLogicStateIfNeeded = function (userId, animalNode) {
        if (!this.logicStates.has(userId)) {
            var initialState = {
                position: cc.v2(animalNode.x, animalNode.y),
                velocity: cc.Vec2.ZERO,
                timestamp: Date.now(),
                logicFrame: this.currentLogicFrame
            };
            this.logicStates.set(userId, [initialState]);
        }
        if (!this.renderStates.has(userId)) {
            var initialRenderState = {
                position: cc.v2(animalNode.x, animalNode.y),
                velocity: cc.Vec2.ZERO,
                lastLogicFrame: this.currentLogicFrame
            };
            this.renderStates.set(userId, initialRenderState);
        }
    };
    /**
     * 初始化方块动画管理器
     */
    GamePageController.prototype.initializeBlockAnimationManager = function () {
        if (!this.blockParent) {
            console.warn("方块父节点未设置，跳过方块动画管理器初始化");
            return;
        }
        this.blockAnimationManager = BlockAnimationManager_1.BlockAnimationManager.getInstance();
        this.blockAnimationManager.initialize(this.blockParent);
    };
    /**
     * 初始化游戏事件处理器
     */
    GamePageController.prototype.initializeGameEventHandler = function () {
        this.gameEventHandler = new SimpleGameEventHandler_1.SimpleGameEventHandler();
        this.gameEventHandler.setGamePageController(this);
        this.gameEventHandler.registerWebSocketHandlers();
    };
    /**
     * 处理游戏事件消息（从WebSocket接收）
     * 根据API文档的统一数字事件系统处理
     */
    GamePageController.prototype.handleGameEvent = function (eventData) {
        // 如果方块动画管理器未初始化，尝试初始化
        if (!this.blockAnimationManager) {
            this.initializeBlockAnimationManager();
        }
        // 只有方块相关事件(3-5)才委托给方块动画管理器处理，避免玩家事件干扰方块动画
        if (eventData.eventType === 3 || eventData.eventType === 4 || eventData.eventType === 5) {
            if (this.blockAnimationManager) {
                this.blockAnimationManager.handleGameEvent(eventData);
            }
            else {
                console.warn("方块动画管理器未初始化，无法处理游戏事件");
            }
        }
        // 根据事件类型执行额外逻辑
        this.handleAdditionalEventLogic(eventData);
    };
    /**
     * 为渲染时间点找到最佳的逻辑状态
     */
    GamePageController.prototype.findBestStateForRenderTime = function (logicStates, renderTime) {
        if (logicStates.length === 0)
            return null;
        // 如果渲染时间比所有状态都早，使用最早的状态
        if (renderTime <= logicStates[0].timestamp) {
            return logicStates[0];
        }
        // 如果渲染时间比所有状态都晚，使用最新的状态（但要小心外插值）
        var latestState = logicStates[logicStates.length - 1];
        if (renderTime >= latestState.timestamp) {
            // 检查时间差是否在可接受范围内
            var timeDiff = renderTime - latestState.timestamp;
            if (timeDiff <= this.jitterBufferSize) {
                return latestState;
            }
        }
        // 找到时间最接近的状态
        var bestState = logicStates[0];
        var minTimeDiff = Math.abs(renderTime - bestState.timestamp);
        for (var _i = 0, logicStates_1 = logicStates; _i < logicStates_1.length; _i++) {
            var state = logicStates_1[_i];
            var timeDiff = Math.abs(renderTime - state.timestamp);
            if (timeDiff < minTimeDiff) {
                minTimeDiff = timeDiff;
                bestState = state;
            }
        }
        return bestState;
    };
    GamePageController.prototype.handleAdditionalEventLogic = function (eventData) {
        switch (eventData.eventType) {
            case 1: // GameEventWaiting - 等待阶段
                this.currentGamePhase = 1;
                this.handleCountdownStart(eventData);
                break;
            case 2: // GameEventRoundWaiting - 回合等待
                this.currentGamePhase = 2;
                this.handleCountdownEnd(eventData);
                break;
            case 3: // GameEventShaking - 松动阶段
                this.currentGamePhase = 3;
                if (eventData.blocks) {
                    this.updateFallenBlocks(eventData.blocks);
                }
                break;
            case 4: // GameEventFalling - 掉落阶段
                this.currentGamePhase = 4;
                if (eventData && eventData.blocks) {
                    this.updateFallenBlocks(eventData.blocks);
                }
                break;
            case 5: // GameEventRestoring - 补齐阶段
                this.currentGamePhase = 5;
                if (eventData && eventData.blocks) {
                    this.updateFallenBlocks(eventData.blocks);
                }
                break;
            case 6: // GameEventEnding - 结束阶段
                this.currentGamePhase = 6;
                break;
            case 7: // GameEventPlayerDeath - 玩家死亡
                this.handlePlayerStatusEvent(eventData);
                break;
            case 8: // GameEventPlayerDisconnect - 玩家断线
                this.handlePlayerStatusEvent(eventData);
                break;
            case 9: // GameEventPlayerLeave - 离开游戏
                this.handlePlayerStatusEvent(eventData);
                break;
            case 10: // GameEventPlayerReconnect - 断线重连
                this.handlePlayerStatusEvent(eventData);
                break;
            case 11: // GameEventShowcase - 难度升级展示
                this.handleShowcaseEvent(eventData);
                break;
            default:
                break;
        }
    };
    /**
     * 处理玩家状态事件（死亡、断线、退出、重连）
     * @param eventData 游戏事件数据
     */
    GamePageController.prototype.handlePlayerStatusEvent = function (eventData) {
        if (!eventData.userId) {
            console.warn("玩家状态事件缺少userId");
            return;
        }
        // 🎯 特殊处理eventType:10（重连事件）：根据isAlive字段决定显示状态
        if (eventData.eventType === 10) {
            if (this.gameLayoutController) {
                // 先隐藏断线标识
                this.gameLayoutController.hideDisconnectStatus(eventData.userId);
                // 根据isAlive字段决定是否显示死亡标识
                if (eventData.isAlive === false) {
                    this.gameLayoutController.showPlayerStatus(eventData.userId, 7); // 显示死亡
                }
                else {
                    this.gameLayoutController.hideAllPlayerStatus(eventData.userId); // 隐藏所有状态
                }
            }
        }
        else {
            // 其他事件类型（7-死亡, 8-断线, 9-退出）正常处理
            if (this.gameLayoutController) {
                this.gameLayoutController.showPlayerStatus(eventData.userId, eventData.eventType);
            }
            else {
                console.warn("GameLayoutController未设置，无法显示玩家状态");
            }
        }
    };
    /**
     * 根据用户ID查找玩家头像控制器
     * @param userId 用户ID
     * @returns 玩家头像控制器或null
     */
    GamePageController.prototype.findPlayerAvatarController = function (userId) {
        for (var i = 0; i < this.playerAvatarControllers.length; i++) {
            var controller = this.playerAvatarControllers[i];
            if (controller) {
                var controllerUserId = controller.getUserId();
                if (controllerUserId === userId) {
                    return controller;
                }
            }
            else {
            }
        }
        return null;
    };
    /**
     * 根据OnlineStateEvent显示玩家状态（用于断线重连）
     * @param userId 用户ID
     * @param onlineStateEvent OnlineStateEvent值
     */
    GamePageController.prototype.showPlayerStatusByOnlineStateEvent = function (userId, onlineStateEvent) {
        // 通过GameLayoutController来管理玩家状态显示
        if (this.gameLayoutController) {
            this.gameLayoutController.showPlayerStatusByOnlineStateEvent(userId, onlineStateEvent);
        }
        else {
            console.warn("GameLayoutController未设置，无法显示玩家状态");
        }
    };
    /**
     * 初始化所有玩家头像状态节点
     */
    GamePageController.prototype.initializeAllPlayerAvatarStatus = function () {
        for (var _i = 0, _a = this.playerAvatarControllers; _i < _a.length; _i++) {
            var controller = _a[_i];
            if (controller) {
                controller.initializeStatusNodes();
            }
        }
    };
    /**
     * 处理断线重连时的用户数据，根据OnlineStateEvent显示状态
     * @param users 用户数据数组（来自EnterRoom API）
     */
    GamePageController.prototype.handleReconnectionUserData = function (users) {
        var _a, _b;
        // 通过GameLayoutController来处理断线重连数据
        if (this.gameLayoutController) {
            this.gameLayoutController.handleReconnectionUserData(users);
        }
        else {
            console.warn("GameLayoutController未设置，无法处理断线重连数据");
        }
        // 断线重连回来：如果本地玩家已死亡，则显示GameOver并保持禁用输入
        try {
            var myUserId_1 = (_b = (_a = GlobalBean_1.GlobalBean.GetInstance().loginData) === null || _a === void 0 ? void 0 : _a.userInfo) === null || _b === void 0 ? void 0 : _b.userId;
            if (myUserId_1 && Array.isArray(users)) {
                var me = users.find(function (u) { return u && u.userId === myUserId_1; });
                // 后端约定：bpStatus 1=存活 2=死亡
                if (me && me.bpStatus === 2) {
                    // 标记为已掉落
                    this.fallenPlayers.add(myUserId_1);
                    // 禁用输入
                    this.isInputBlocked = true;
                    if (this.joystickController) {
                        this.joystickController.setInteractable(false);
                    }
                    // 显示GameOver（若为倒数第二个死亡，即场上仅剩1人生还，则不显示）
                    var alive = this.getAlivePlayerCount();
                    if (alive > 1) {
                        this.showGameOver();
                    }
                }
            }
        }
        catch (e) {
            console.warn("处理断线重连死亡状态时出错", e);
        }
    };
    /**
     * 测试方法：手动触发玩家状态显示（调试用）
     * 可在控制台调用：gamePage.testPlayerStatus("userId", 7)
     */
    GamePageController.prototype.testPlayerStatus = function (userId, eventType) {
        // 直接通过GameLayoutController测试
        if (this.gameLayoutController) {
            this.gameLayoutController.showPlayerStatus(userId, eventType);
        }
        else {
            console.warn("GameLayoutController未设置，无法测试玩家状态");
        }
    };
    /**
     * 调试方法：列出所有玩家头像控制器信息
     */
    GamePageController.prototype.debugPlayerControllers = function () {
        // 通过GameLayoutController获取调试信息
        if (this.gameLayoutController) {
            this.gameLayoutController.debugPlayerControllers();
        }
        else {
            console.warn("GameLayoutController未设置，无法获取调试信息");
        }
        // 同时显示原有的数组信息（如果有的话）
    };
    /**
     * 测试方法：模拟GameFrame消息（调试用）
     */
    GamePageController.prototype.testGameFrameEvent = function (eventType, userId) {
        var mockGameFrameData = {
            serverFrameId: 1,
            serverTime: Date.now(),
            gameEvents: [{
                    eventType: eventType,
                    userId: userId,
                    currentRound: 1,
                    countDown: 0,
                    randomSeed: 0,
                    message: "\u6D4B\u8BD5\u4E8B\u4EF6" + eventType
                }],
            playerPositions: []
        };
        this.handleGameFrame(mockGameFrameData);
    };
    /**
     * 手动强制解锁（调试用）。可在控制台调用：gamePage.forceUnlockInput()
     */
    GamePageController.prototype.forceUnlockInput = function () {
        this.isCountdownActive = false;
        this.isWaitingPhase = false;
        this.isInputBlocked = false;
        if (this.countdownTimer) {
            clearTimeout(this.countdownTimer);
            this.countdownTimer = 0;
        }
        if (this.countdownNode) {
            this.countdownNode.active = false;
        }
        // 但如果本地玩家已死亡，则保持摇杆禁用状态
        if (this.joystickController && !this.isLocalPlayerDead()) {
            this.joystickController.setInteractable(true);
            // 摇杆启用时隐藏提示动画
            this.hideTishiAnimation();
        }
    };
    /**
     * 获取方块动画管理器实例（供外部调用）
     */
    GamePageController.prototype.getBlockAnimationManager = function () {
        return this.blockAnimationManager;
    };
    /**
     * 清理资源时同时清理方块动画管理器
     */
    GamePageController.prototype.onDestroy = function () {
        // 清理方块动画管理器
        if (this.blockAnimationManager) {
            this.blockAnimationManager.cleanup();
            this.blockAnimationManager = null;
        }
        // 清理平滑移动系统
        this.targetPositions.clear();
        this.playerMovingStates.clear();
        this.reconciliationStates.clear();
        // 清理待机动画定时器
        this.clearIdleAnimationTimer();
        // 组件销毁时隐藏回合数显示
        this.hideRoundDisplay();
    };
    // ========== 玩家掉落检测系统 ==========
    /**
     * 格子坐标转像素坐标（格子中心）
     * 将格子坐标转换为对应格子中心的像素坐标
     */
    GamePageController.prototype.gridToPixel = function (gridX, gridY) {
        var pixelX = this.BOARD_CONFIG.LEFT_X + (gridX + 0.5) * this.BOARD_CONFIG.GRID_SIZE;
        var pixelY = this.BOARD_CONFIG.BOTTOM_Y + (gridY + 0.5) * this.BOARD_CONFIG.GRID_SIZE;
        return { pixelX: pixelX, pixelY: pixelY };
    };
    /**
     * 像素坐标转格子坐标
     * 大地图范围: 左下角(-359,-343) 到 右上角(359,375)
     * 9x9格子，每格79x79像素
     */
    GamePageController.prototype.pixelToGrid = function (pixelX, pixelY) {
        // 与后端一致：使用总宽/高除以9计算格子宽/高
        var totalWidth = this.BOARD_CONFIG.RIGHT_X - this.BOARD_CONFIG.LEFT_X; // 718
        var totalHeight = this.BOARD_CONFIG.TOP_Y - this.BOARD_CONFIG.BOTTOM_Y; // 718
        var gridWidth = totalWidth / this.BOARD_CONFIG.GRID_COUNT; // 79.777...
        var gridHeight = totalHeight / this.BOARD_CONFIG.GRID_COUNT; // 79.777...
        var offsetX = pixelX - this.BOARD_CONFIG.LEFT_X;
        var offsetY = pixelY - this.BOARD_CONFIG.BOTTOM_Y;
        var gridX = Math.floor(offsetX / gridWidth);
        var gridY = Math.floor(offsetY / gridHeight);
        // 与后端一致：超出范围返回(-1, -1)
        if (gridX < 0 || gridX >= this.BOARD_CONFIG.GRID_COUNT || gridY < 0 || gridY >= this.BOARD_CONFIG.GRID_COUNT) {
            return { gridX: -1, gridY: -1 };
        }
        return { gridX: gridX, gridY: gridY };
    };
    /**
     * 检查玩家是否出了棋盘边界 - 修复2：85%位置判定
     */
    GamePageController.prototype.isPlayerOutOfBounds = function (playerX, playerY) {
        // 与后端一致：直接使用坐标与边界比较
        return (playerX < this.BOARD_CONFIG.LEFT_X ||
            playerX > this.BOARD_CONFIG.RIGHT_X ||
            playerY < this.BOARD_CONFIG.BOTTOM_Y ||
            playerY > this.BOARD_CONFIG.TOP_Y);
    };
    /**
     * 检查玩家90%体积是否在安全格子上 - 修复版本
     */
    GamePageController.prototype.isPlayerOnSafeGround = function (playerX, playerY) {
        var survivalSize = this.BOARD_CONFIG.PLAYER_SIZE * this.BOARD_CONFIG.SURVIVAL_RATIO; // 54像素
        var halfSurvivalSize = survivalSize / 2; // 27像素
        // 计算玩家90%体积的四个边界点
        var leftX = playerX - halfSurvivalSize;
        var rightX = playerX + halfSurvivalSize;
        var bottomY = playerY - halfSurvivalSize;
        var topY = playerY + halfSurvivalSize;
        // 获取90%体积四个角点覆盖的格子
        var corners = [
            this.pixelToGrid(leftX, bottomY),
            this.pixelToGrid(rightX, bottomY),
            this.pixelToGrid(leftX, topY),
            this.pixelToGrid(rightX, topY) // 右上角
        ];
        // 收集所有被覆盖的格子（去重）
        var coveredGrids = new Set();
        // 找到边界范围
        var minGridX = Math.min.apply(Math, corners.map(function (c) { return c.gridX; }));
        var maxGridX = Math.max.apply(Math, corners.map(function (c) { return c.gridX; }));
        var minGridY = Math.min.apply(Math, corners.map(function (c) { return c.gridY; }));
        var maxGridY = Math.max.apply(Math, corners.map(function (c) { return c.gridY; }));
        var totalOverlapArea = 0;
        var safeOverlapArea = 0;
        var survivalAreaSize = survivalSize * survivalSize; // 90%体积的总面积
        // 遍历边界范围内的所有格子
        for (var gridX = minGridX; gridX <= maxGridX; gridX++) {
            for (var gridY = minGridY; gridY <= maxGridY; gridY++) {
                // 检查格子是否在有效范围内
                if (gridX < 0 || gridX >= this.BOARD_CONFIG.GRID_COUNT ||
                    gridY < 0 || gridY >= this.BOARD_CONFIG.GRID_COUNT) {
                    // 超出棋盘的部分算作不安全区域
                    var overlapArea_1 = this.calculateOverlapArea(gridX, gridY, playerX, playerY, halfSurvivalSize);
                    totalOverlapArea += overlapArea_1;
                    continue;
                }
                // 计算这个格子与玩家90%体积的重叠面积
                var overlapArea = this.calculateOverlapArea(gridX, gridY, playerX, playerY, halfSurvivalSize);
                if (overlapArea > 0) {
                    var gridKey = gridX + "," + gridY;
                    coveredGrids.add(gridKey);
                    totalOverlapArea += overlapArea;
                    // 如果这个格子是安全的，累加安全面积
                    if (!this.fallenBlocks.has(gridKey)) {
                        safeOverlapArea += overlapArea;
                    }
                }
            }
        }
        // 如果没有覆盖任何格子，说明玩家悬空
        if (totalOverlapArea === 0) {
            return false;
        }
        // 计算安全面积比例
        var safeRatio = safeOverlapArea / totalOverlapArea;
        var requiredSafeRatio = 0.5; // 需要85%的体积在安全区域
        if (safeRatio >= requiredSafeRatio) {
            return true;
        }
        else {
            return false;
        }
    };
    /**
     * 计算格子与玩家90%体积区域的重叠面积
     */
    GamePageController.prototype.calculateOverlapArea = function (gridX, gridY, playerX, playerY, halfSurvivalSize) {
        // 计算格子的像素边界
        var gridPixelLeft = this.BOARD_CONFIG.LEFT_X + gridX * this.BOARD_CONFIG.GRID_SIZE;
        var gridPixelRight = gridPixelLeft + this.BOARD_CONFIG.GRID_SIZE;
        var gridPixelBottom = this.BOARD_CONFIG.BOTTOM_Y + gridY * this.BOARD_CONFIG.GRID_SIZE;
        var gridPixelTop = gridPixelBottom + this.BOARD_CONFIG.GRID_SIZE;
        // 计算玩家90%体积的边界
        var playerLeft = playerX - halfSurvivalSize;
        var playerRight = playerX + halfSurvivalSize;
        var playerBottom = playerY - halfSurvivalSize;
        var playerTop = playerY + halfSurvivalSize;
        // 计算重叠区域的边界
        var overlapLeft = Math.max(gridPixelLeft, playerLeft);
        var overlapRight = Math.min(gridPixelRight, playerRight);
        var overlapBottom = Math.max(gridPixelBottom, playerBottom);
        var overlapTop = Math.min(gridPixelTop, playerTop);
        // 如果没有重叠，返回0
        if (overlapLeft >= overlapRight || overlapBottom >= overlapTop) {
            return 0;
        }
        // 计算重叠面积
        var overlapWidth = overlapRight - overlapLeft;
        var overlapHeight = overlapTop - overlapBottom;
        return overlapWidth * overlapHeight;
    };
    /**
     * 检查格子是否与玩家90%体积区域重叠
     */
    GamePageController.prototype.isGridOverlappingWithSurvivalArea = function (gridX, gridY, playerX, playerY, halfSurvivalSize) {
        // 计算格子的像素边界
        var gridPixelLeft = this.BOARD_CONFIG.LEFT_X + gridX * this.BOARD_CONFIG.GRID_SIZE;
        var gridPixelRight = gridPixelLeft + this.BOARD_CONFIG.GRID_SIZE;
        var gridPixelBottom = this.BOARD_CONFIG.BOTTOM_Y + gridY * this.BOARD_CONFIG.GRID_SIZE;
        var gridPixelTop = gridPixelBottom + this.BOARD_CONFIG.GRID_SIZE;
        // 计算玩家90%体积的边界
        var playerLeft = playerX - halfSurvivalSize;
        var playerRight = playerX + halfSurvivalSize;
        var playerBottom = playerY - halfSurvivalSize;
        var playerTop = playerY + halfSurvivalSize;
        // 检查是否有重叠（矩形相交检测）
        var hasOverlap = !(playerRight <= gridPixelLeft || // 玩家在格子左边
            playerLeft >= gridPixelRight || // 玩家在格子右边
            playerTop <= gridPixelBottom || // 玩家在格子下边
            playerBottom >= gridPixelTop // 玩家在格子上边
        );
        return hasOverlap;
    };
    /**
     * 检查玩家是否应该掉落
     */
    GamePageController.prototype.shouldPlayerFall = function (userId, playerX, playerY) {
        // 如果玩家已经掉落，不重复检查
        if (this.fallenPlayers.has(userId)) {
            return false;
        }
        // 检查是否出了棋盘边界（任何情况下都执行）
        if (this.isPlayerOutOfBounds(playerX, playerY)) {
            return true;
        }
        // 只在掉落阶段(4)和补齐阶段(5)检查格子掉落
        if (this.currentGamePhase === 4 || this.currentGamePhase === 5) {
            if (!this.isPlayerOnSafeGround(playerX, playerY)) {
                return true;
            }
        }
        return false;
    };
    /**
     * 执行玩家掉落操作
     */
    GamePageController.prototype.executePlayerFall = function (userId, options) {
        var _a, _b;
        // 标记玩家已掉落和正在掉落（位置锁定）
        this.fallenPlayers.add(userId);
        this.fallingPlayers.add(userId);
        // 获取玩家动物节点
        var animalNode = this.allPlayerAnimals.get(userId);
        if (!animalNode) {
            console.error("\u627E\u4E0D\u5230\u73A9\u5BB6 " + userId + " \u7684\u52A8\u7269\u8282\u70B9");
            return;
        }
        // 获取玩家数据
        var playerData = this.allPlayerData.get(userId);
        if (!playerData) {
            console.error("\u627E\u4E0D\u5230\u73A9\u5BB6 " + userId + " \u7684\u6570\u636E");
            return;
        }
        // 计算掉落偏移量（优先使用事件携带的权威像素坐标，保证各端方向一致）
        var baseX = (options && typeof options.pixelX === 'number') ? options.pixelX : animalNode.x;
        var baseY = (options && typeof options.pixelY === 'number') ? options.pixelY : animalNode.y;
        // 🔧 修复视觉不一致：如果服务器提供了权威坐标，先同步玩家位置到权威位置
        if (options && typeof options.pixelX === 'number' && typeof options.pixelY === 'number') {
            var currentX = animalNode.x;
            var currentY = animalNode.y;
            var deltaX = Math.abs(currentX - baseX);
            var deltaY = Math.abs(currentY - baseY);
            // 为了确保掉落动画的完全一致性，总是同步到服务器权威位置
            // 即使差异较小，也进行同步，确保动画起始位置精确
            if (deltaX > 1 || deltaY > 1) {
                console.log("\uD83D\uDD27 \u4F4D\u7F6E\u540C\u6B65 - \u73A9\u5BB6" + userId + ": \u5BA2\u6237\u7AEF(" + currentX.toFixed(1) + ", " + currentY.toFixed(1) + ") -> \u670D\u52A1\u5668(" + baseX.toFixed(1) + ", " + baseY.toFixed(1) + ")");
                animalNode.x = baseX;
                animalNode.y = baseY;
            }
        }
        var fallOffset = { x: 0, y: 0 };
        // 🎯 完全按照后端返回的GridX、GridY来计算偏移
        if (options && typeof options.gridX === 'number' && typeof options.gridY === 'number') {
            var FALL_OFFSET_DISTANCE = 30;
            fallOffset = this.calculateGridCenterFallOffsetUniversal(baseX, baseY, options.gridX, options.gridY, FALL_OFFSET_DISTANCE);
            console.log("\uD83C\uDFAF \u4F7F\u7528\u540E\u7AEFGridX=" + options.gridX + ", GridY=" + options.gridY + " - \u73A9\u5BB6\u4F4D\u7F6E: (" + baseX.toFixed(1) + ", " + baseY.toFixed(1) + "), \u504F\u79FB: (" + fallOffset.x.toFixed(1) + ", " + fallOffset.y.toFixed(1) + ")");
        }
        else {
            // 如果没有后端GridX、GridY，则不做任何偏移（保持原位下落）
            fallOffset = { x: 0, y: 0 };
            console.log("\u26A0\uFE0F \u6CA1\u6709\u540E\u7AEFGridX/GridY\u53C2\u6570\uFF0C\u4FDD\u6301\u539F\u4F4D\u4E0B\u843D - \u73A9\u5BB6\u4F4D\u7F6E: (" + baseX.toFixed(1) + ", " + baseY.toFixed(1) + ")");
        }
        // 【修复1】掉落动画触发前先隐藏bg节点
        this.hideBgNodeBeforeFall(animalNode, userId);
        // 播放掉落动画（带偏移）
        this.playFallAnimation(animalNode, playerData.playerType, fallOffset);
        // 如果是本地玩家，禁止摇杆输入并清零速度，避免掉落时仍可操作
        var myUserId = (_b = (_a = GlobalBean_1.GlobalBean.GetInstance().loginData) === null || _a === void 0 ? void 0 : _a.userInfo) === null || _b === void 0 ? void 0 : _b.userId;
        if (userId === myUserId) {
            this.isInputBlocked = true;
            this.currentPlayerVelocity = cc.Vec2.ZERO;
            if (this.joystickController) {
                this.joystickController.setInteractable(false);
            }
            // 显示GameOver节点（若为倒数第二个死亡，即场上仅剩1人生还，则不显示）
            var alive = this.getAlivePlayerCount();
            if (alive > 1) {
                this.showGameOver();
            }
        }
    };
    /**
     * 计算掉落偏移量
     * 根据掉落类型和位置确定动物预制体的偏移方向
     */
    GamePageController.prototype.calculateFallOffset = function (playerX, playerY) {
        var OFFSET_DISTANCE = 30; // 偏移距离（像素）
        // 检查是否是边界掉落
        if (this.isPlayerOutOfBounds(playerX, playerY)) {
            return this.calculateBoundaryFallOffset(playerX, playerY, OFFSET_DISTANCE);
        }
        // 格子掉落：在掉落阶段且不在安全地面上
        if ((this.currentGamePhase === 4 || this.currentGamePhase === 5) &&
            !this.isPlayerOnSafeGround(playerX, playerY)) {
            // 在GameEventFalling阶段，使用新的判断逻辑
            if (this.currentGamePhase === 4) {
                return this.calculateGridFallOffsetImproved(playerX, playerY, OFFSET_DISTANCE);
            }
            else {
                // 补齐阶段直接掉落
                return { x: 0, y: 0 };
            }
        }
        // 默认情况（不应该发生）
        return { x: 0, y: 0 };
    };
    /**
     * 计算边界掉落偏移量
     */
    GamePageController.prototype.calculateBoundaryFallOffset = function (playerX, playerY, offsetDistance) {
        var leftEdge = playerX - this.BOARD_CONFIG.PLAYER_SIZE * 0.25;
        var rightEdge = playerX + this.BOARD_CONFIG.PLAYER_SIZE * 0.25;
        var bottomEdge = playerY - this.BOARD_CONFIG.PLAYER_SIZE * 0.25;
        var topEdge = playerY + this.BOARD_CONFIG.PLAYER_SIZE * 0.25;
        var offsetX = 0;
        var offsetY = 0;
        // 检查各个边界（方向取反：上<->下，左<->右）
        if (leftEdge < this.BOARD_CONFIG.LEFT_X) {
            // 左边界出界，改为向左偏移
            offsetX = -offsetDistance;
        }
        else if (rightEdge > this.BOARD_CONFIG.RIGHT_X) {
            // 右边界出界，改为向右偏移
            offsetX = offsetDistance;
        }
        if (bottomEdge < this.BOARD_CONFIG.BOTTOM_Y) {
            // 下边界出界，改为向下偏移
            offsetY = -offsetDistance;
        }
        else if (topEdge > this.BOARD_CONFIG.TOP_Y) {
            // 上边界出界，改为向上偏移
            offsetY = offsetDistance;
        }
        return { x: offsetX, y: offsetY };
    };
    /**
     * 计算格子内掉落偏移量（朝致死格子中心偏移）
     * 用于处理玩家死亡时朝指定格子中心的偏移
     */
    GamePageController.prototype.calculateGridCenterFallOffset = function (playerX, playerY, deathGridX, deathGridY, offsetDistance) {
        // 获取致死格子的中心坐标
        var gridCenter = this.gridToPixel(deathGridX, deathGridY);
        // 计算玩家当前位置到格子中心的方向向量
        var dx = gridCenter.pixelX - playerX;
        var dy = gridCenter.pixelY - playerY;
        // 计算距离
        var distance = Math.sqrt(dx * dx + dy * dy);
        // 如果距离为0（玩家已在格子中心），则不偏移
        if (distance === 0) {
            return { x: 0, y: 0 };
        }
        // 归一化方向向量并应用偏移距离
        var normalizedX = dx / distance;
        var normalizedY = dy / distance;
        var offsetX = normalizedX * offsetDistance;
        var offsetY = normalizedY * offsetDistance;
        return { x: offsetX, y: offsetY };
    };
    /**
     * 通用格子中心偏移计算（支持出界格子）
     * 完全按照后端返回的GridX、GridY计算偏移，支持虚拟出界格子
     */
    GamePageController.prototype.calculateGridCenterFallOffsetUniversal = function (playerX, playerY, gridX, gridY, offsetDistance) {
        // 🎯 支持出界格子：如果GridX或GridY超出棋盘范围，虚拟创建格子
        // 计算虚拟格子的中心坐标（支持负数和超出范围的格子）
        var virtualGridCenterX = this.BOARD_CONFIG.LEFT_X + (gridX + 0.5) * this.BOARD_CONFIG.GRID_SIZE;
        var virtualGridCenterY = this.BOARD_CONFIG.BOTTOM_Y + (gridY + 0.5) * this.BOARD_CONFIG.GRID_SIZE;
        // 计算玩家当前位置到虚拟格子中心的方向向量
        var dx = virtualGridCenterX - playerX;
        var dy = virtualGridCenterY - playerY;
        // 计算距离
        var distance = Math.sqrt(dx * dx + dy * dy);
        // 如果距离为0，返回零偏移（避免除零错误）
        if (distance === 0) {
            console.log("\uD83C\uDFAF \u901A\u7528\u683C\u5B50\u504F\u79FB - \u76EE\u6807\u683C\u5B50: (" + gridX + ", " + gridY + "), \u73A9\u5BB6\u5DF2\u5728\u683C\u5B50\u4E2D\u5FC3\uFF0C\u504F\u79FB: (0, 0)");
            return { x: 0, y: 0 };
        }
        // 归一化方向向量并应用偏移距离
        var normalizedX = dx / distance;
        var normalizedY = dy / distance;
        var offsetX = normalizedX * offsetDistance;
        var offsetY = normalizedY * offsetDistance;
        // 判断是否为出界格子
        var isOutOfBounds = gridX < 0 || gridX >= this.BOARD_CONFIG.GRID_COUNT || gridY < 0 || gridY >= this.BOARD_CONFIG.GRID_COUNT;
        var boundaryType = isOutOfBounds ? "出界" : "棋盘内";
        console.log("\uD83C\uDFAF " + boundaryType + "\u683C\u5B50\u504F\u79FB - \u76EE\u6807\u683C\u5B50: (" + gridX + ", " + gridY + "), \u865A\u62DF\u4E2D\u5FC3: (" + virtualGridCenterX.toFixed(1) + ", " + virtualGridCenterY.toFixed(1) + "), \u504F\u79FB: (" + offsetX.toFixed(1) + ", " + offsetY.toFixed(1) + ")");
        return { x: offsetX, y: offsetY };
    };
    /**
     * 改进的格子掉落偏移计算（GameEventFalling阶段）
     * 使用更精确的算法区分两种情况
     */
    GamePageController.prototype.calculateGridFallOffsetImproved = function (playerX, playerY, offsetDistance) {
        // 获取玩家当前所在的主要格子坐标
        var playerGrid = this.pixelToGrid(playerX, playerY);
        // 计算玩家90%体积覆盖的所有格子
        var survivalSize = this.BOARD_CONFIG.PLAYER_SIZE * this.BOARD_CONFIG.SURVIVAL_RATIO;
        var halfSurvivalSize = survivalSize / 2;
        var leftX = playerX - halfSurvivalSize;
        var rightX = playerX + halfSurvivalSize;
        var bottomY = playerY - halfSurvivalSize;
        var topY = playerY + halfSurvivalSize;
        // 获取覆盖的格子范围
        var leftGrid = this.pixelToGrid(leftX, bottomY);
        var rightGrid = this.pixelToGrid(rightX, topY);
        var minGridX = Math.min(leftGrid.gridX, rightGrid.gridX);
        var maxGridX = Math.max(leftGrid.gridX, rightGrid.gridX);
        var minGridY = Math.min(leftGrid.gridY, rightGrid.gridY);
        var maxGridY = Math.max(leftGrid.gridY, rightGrid.gridY);
        // 统计覆盖的格子状态
        var totalCoveredGrids = 0;
        var fallenCoveredGrids = 0;
        var safeCoveredGrids = 0;
        for (var gridX = minGridX; gridX <= maxGridX; gridX++) {
            for (var gridY = minGridY; gridY <= maxGridY; gridY++) {
                if (gridX >= 0 && gridX < this.BOARD_CONFIG.GRID_COUNT &&
                    gridY >= 0 && gridY < this.BOARD_CONFIG.GRID_COUNT) {
                    // 检查这个格子与玩家90%体积是否有重叠
                    if (this.isGridOverlappingWithSurvivalArea(gridX, gridY, playerX, playerY, halfSurvivalSize)) {
                        totalCoveredGrids++;
                        var gridKey = gridX + "," + gridY;
                        if (this.fallenBlocks.has(gridKey)) {
                            fallenCoveredGrids++;
                        }
                        else {
                            safeCoveredGrids++;
                        }
                    }
                }
            }
        }
        // 判断逻辑：
        // 1. 如果玩家覆盖的格子中，掉落格子占比 > 70% -> 站在掉落区域，不需要偏移
        // 2. 如果玩家覆盖的格子中，掉落格子占比 ≤ 70% -> 需要偏移
        var fallenRatio = totalCoveredGrids > 0 ? fallenCoveredGrids / totalCoveredGrids : 0;
        if (fallenRatio > 0.7) {
            // 超过70%的覆盖格子已掉落 -> 站在掉落区域，不需要偏移
            return { x: 0, y: 0 };
        }
        else {
            // 掉落格子占比不足70% -> 需要偏移
            return this.calculateOffsetTowardsFallenGrid(playerX, playerY, playerGrid, offsetDistance);
        }
    };
    /**
     * 计算格子掉落偏移量（GameEventFalling阶段）
     * 区分两种情况：站在格子上掉落 vs 走向已掉落的格子
     */
    GamePageController.prototype.calculateGridFallOffset = function (playerX, playerY, offsetDistance) {
        // 获取玩家当前所在的格子坐标
        var playerGrid = this.pixelToGrid(playerX, playerY);
        var currentGridKey = playerGrid.gridX + "," + playerGrid.gridY;
        // 检查当前格子是否已经在掉落列表中
        var isCurrentGridFallen = this.fallenBlocks.has(currentGridKey);
        // 检查玩家是否在安全地面上（这个方法会检查玩家90%体积覆盖的所有格子）
        var isOnSafeGround = this.isPlayerOnSafeGround(playerX, playerY);
        // 判断掉落类型的逻辑：
        // 1. 如果玩家不在安全地面上，且当前格子已掉落 -> 走向已掉落格子（需要偏移）
        // 2. 如果玩家不在安全地面上，但当前格子未掉落 -> 站在格子上一起掉落（不偏移）
        if (!isOnSafeGround && isCurrentGridFallen) {
            // 情况2：走向已掉落的格子区域 - 需要向掉落方向偏移
            return this.calculateOffsetTowardsFallenGrid(playerX, playerY, playerGrid, offsetDistance);
        }
        else {
            // 情况1：站在格子上时格子掉落，或者其他情况 - 不偏移，直接掉落
            return { x: 0, y: 0 };
        }
    };
    /**
     * 计算向已掉落格子方向的偏移
     * 玩家走向已经掉落的格子区域时，向该掉落区域偏移
     */
    GamePageController.prototype.calculateOffsetTowardsFallenGrid = function (playerX, playerY, playerGrid, offsetDistance) {
        // 计算当前格子的中心点
        var gridCenterX = this.BOARD_CONFIG.LEFT_X + (playerGrid.gridX + 0.5) * this.BOARD_CONFIG.GRID_SIZE;
        var gridCenterY = this.BOARD_CONFIG.BOTTOM_Y + (playerGrid.gridY + 0.5) * this.BOARD_CONFIG.GRID_SIZE;
        // 计算玩家相对于格子中心的偏移方向
        var dx = playerX - gridCenterX;
        var dy = playerY - gridCenterY;
        var offsetX = 0;
        var offsetY = 0;
        // 根据相对位置决定偏移方向（与之前相反：左->右，右->左，上->下，下->上）
        if (Math.abs(dx) > Math.abs(dy)) {
            // 水平方向偏移更明显 -> 方向取反：左改为右，右改为左
            if (dx < 0) {
                offsetX = offsetDistance; // 原左，改为向右
            }
            else {
                offsetX = -offsetDistance; // 原右，改为向左
            }
        }
        else {
            // 垂直方向偏移更明显 -> 方向取反：上改为下，下改为上
            if (dy < 0) {
                offsetY = offsetDistance; // 原下，改为向上
            }
            else {
                offsetY = -offsetDistance; // 原上，改为向下
            }
        }
        // 如果偏移量太小，使用默认的向下偏移（方向取反）
        if (offsetX === 0 && offsetY === 0) {
            offsetY = -offsetDistance; // 默认向下偏移
        }
        return { x: offsetX, y: offsetY };
    };
    /**
     * 从动物节点获取对应的用户ID
     */
    GamePageController.prototype.getUserIdFromAnimalNode = function (animalNode) {
        var foundUserId = null;
        this.allPlayerAnimals.forEach(function (node, userId) {
            if (node === animalNode) {
                foundUserId = userId;
            }
        });
        return foundUserId;
    };
    /**
     * 播放动物掉落动画
     */
    GamePageController.prototype.playFallAnimation = function (animalNode, playerType, fallOffset) {
        var _this = this;
        // 获取动物类型对应的动画名称
        var animalType = this.ANIMAL_TYPES[playerType];
        if (!animalType) {
            console.error("\u672A\u77E5\u7684\u52A8\u7269\u7C7B\u578B: " + playerType);
            return;
        }
        // 构造掉落动画名称: xialuo/动物类型
        var fallAnimationName = "xialuo/" + animalType;
        // 查找dongwu节点
        var dongwuNode = animalNode.getChildByName('dongwu');
        if (!dongwuNode) {
            console.error("\u627E\u4E0D\u5230\u52A8\u7269\u8282\u70B9\u7684dongwu\u5B50\u8282\u70B9");
            return;
        }
        // 获取骨骼动画组件
        var skeleton = dongwuNode.getComponent(sp.Skeleton);
        if (!skeleton) {
            console.error("dongwu\u8282\u70B9\u4E0A\u6CA1\u6709\u9AA8\u9ABC\u52A8\u753B\u7EC4\u4EF6");
            return;
        }
        // 统一控制销毁时间：偏移动画时间(如果有) + 下落动画时间(0.5s) + 0.5s
        var hasOffset = !!(fallOffset && (fallOffset.x !== 0 || fallOffset.y !== 0));
        var offsetDurationSec = hasOffset ? 0.1 : 0.0; // 与下方 tween 保持一致
        var fallAnimDurationSec = 0.5; // 需求指定的下落动画时长
        var extraDelaySec = 0.2; // 额外缓冲
        var totalDelaySec = offsetDurationSec + fallAnimDurationSec + extraDelaySec;
        // 在总时长结束后，统一销毁节点（避免动画未播完就消失）
        this.scheduleOnce(function () {
            if (animalNode && cc.isValid(animalNode)) {
                // 销毁而非仅隐藏，避免残留
                animalNode.destroy();
            }
            // 解除位置锁定（从动物节点获取userId）
            var userId = _this.getUserIdFromAnimalNode(animalNode);
            if (userId) {
                _this.fallingPlayers.delete(userId);
                console.log("\uD83D\uDD13 \u4F4D\u7F6E\u9501\u5B9A\u89E3\u9664 - \u73A9\u5BB6" + userId + "\u6389\u843D\u52A8\u753B\u5B8C\u6210");
            }
        }, totalDelaySec);
        // 应用掉落偏移量
        if (hasOffset) {
            console.log("\uD83C\uDFAC \u64AD\u653E\u6389\u843D\u52A8\u753B - \u504F\u79FB: (" + fallOffset.x.toFixed(1) + ", " + fallOffset.y.toFixed(1) + ")");
            // 计算目标位置
            var originalX = animalNode.x;
            var originalY = animalNode.y;
            var targetX_1 = originalX + fallOffset.x;
            var targetY_1 = originalY + fallOffset.y;
            console.log("\uD83C\uDFAC \u6389\u843D\u52A8\u753B\u4F4D\u7F6E - \u8D77\u59CB: (" + originalX.toFixed(1) + ", " + originalY.toFixed(1) + ") -> \u76EE\u6807: (" + targetX_1.toFixed(1) + ", " + targetY_1.toFixed(1) + ")");
            // 先播放0.1秒的偏移移动动画
            cc.tween(animalNode)
                .to(offsetDurationSec, { x: targetX_1, y: targetY_1 }, { easing: 'quadOut' })
                .call(function () {
                // 偏移移动完成后，确保位置已更新
                animalNode.x = targetX_1;
                animalNode.y = targetY_1;
                // 在新位置播放掉落动画
                _this.playFallAnimationInternal(skeleton, fallAnimationName, animalNode);
            })
                .start();
        }
        else {
            // 无偏移，直接在当前位置播放掉落动画
            this.playFallAnimationInternal(skeleton, fallAnimationName, animalNode);
        }
    };
    /**
     * 内部方法：播放掉落动画
     */
    GamePageController.prototype.playFallAnimationInternal = function (skeleton, fallAnimationName, animalNode) {
        var _this = this;
        try {
            // 清除之前的监听器，防止冲突
            skeleton.setCompleteListener(null);
            // 播放掉落动画
            skeleton.setAnimation(0, fallAnimationName, false);
            // 设置下落动画完成监听器，播放死亡特效
            skeleton.setCompleteListener(function () {
                // 播放死亡音效（根据动物类型播放对应的音效）
                _this.playDeathSoundEffect(animalNode);
                // 播放死亡特效（跟随动物节点当前位置，即偏移后的位置）
                _this.playDeathEffect(animalNode);
            });
        }
        catch (error) {
            console.error("\u64AD\u653E\u6389\u843D\u52A8\u753B\u5931\u8D25: " + fallAnimationName, error);
            // 如果动画播放失败，直接隐藏节点
            if (animalNode && cc.isValid(animalNode)) {
                animalNode.destroy();
            }
        }
    };
    /**
     * 更新掉落的方块信息
     */
    GamePageController.prototype.updateFallenBlocks = function (blocks) {
        if (!blocks)
            return;
        // 清空之前的掉落方块记录
        this.fallenBlocks.clear();
        // 添加新的掉落方块
        for (var _i = 0, blocks_1 = blocks; _i < blocks_1.length; _i++) {
            var block = blocks_1[_i];
            var gridKey = block.x + "," + block.y;
            this.fallenBlocks.add(gridKey);
        }
    };
    /**
     * 将大地图像素坐标转换为方块动画系统的格子坐标
     * 大地图坐标 -> 格子坐标 -> 方块动画系统坐标
     */
    GamePageController.prototype.convertToBlockAnimationGrid = function (mapPixelX, mapPixelY) {
        // 先转换为格子坐标
        var gridPos = this.pixelToGrid(mapPixelX, mapPixelY);
        // 格子坐标系统是一致的，直接返回
        return gridPos;
    };
    /**
     * 检查所有玩家的掉落状态
     */
    GamePageController.prototype.checkAllPlayersFallStatus = function () {
        var _this = this;
        this.allPlayerAnimals.forEach(function (animalNode, userId) {
            if (_this.shouldPlayerFall(userId, animalNode.x, animalNode.y)) {
                _this.executePlayerFall(userId);
            }
        });
    };
    /**
     * 实时掉落检测 - 在每个逻辑帧后执行
     */
    GamePageController.prototype.performRealtimeFallDetection = function () {
        var _this = this;
        // 在掉落阶段（阶段4）期间持续进行完整的掉落检测
        if (this.currentGamePhase === 4) {
            // 掉落阶段：进行完整的掉落检测（边界 + 格子）
            this.allPlayerAnimals.forEach(function (animalNode, userId) {
                if (_this.shouldPlayerFall(userId, animalNode.x, animalNode.y)) {
                    if (!_this.fallenPlayers.has(userId)) {
                        _this.executePlayerFall(userId);
                    }
                }
            });
        }
        else {
            // 非掉落阶段只检查边界
            this.allPlayerAnimals.forEach(function (animalNode, userId) {
                if (_this.isPlayerOutOfBounds(animalNode.x, animalNode.y)) {
                    if (!_this.fallenPlayers.has(userId)) {
                        _this.executePlayerFall(userId);
                    }
                }
            });
        }
    };
    // ========== 掉落检测系统辅助方法 ==========
    /**
     * 重置掉落检测状态（游戏重新开始时调用）
     */
    GamePageController.prototype.resetFallDetectionState = function () {
        this.fallenPlayers.clear();
        this.fallingPlayers.clear(); // 清理掉落中状态
        this.fallenBlocks.clear();
        this.currentGamePhase = 1;
        this.isInputBlocked = false;
        // 重置倒计时状态（但不隐藏倒计时节点，因为游戏开始后可能立即需要倒计时）
        this.isCountdownActive = false;
        this.isWaitingPhase = false;
        this.lastCountdownEventId = "";
        if (this.countdownTimer) {
            clearTimeout(this.countdownTimer);
            this.countdownTimer = 0;
        }
        // 注意：不在这里隐藏倒计时节点，让倒计时事件自己控制显示/隐藏
        // 清空地图背景
        this.clearMapBackgrounds();
        // 清理死亡特效节点
        this.clearDeathEffectNodes();
    };
    /**
     * 清空地图背景
     */
    GamePageController.prototype.clearMapBackgrounds = function () {
        var _this = this;
        var backgroundNodes = ['game_bg', 'bg_up', 'bg_down'];
        backgroundNodes.forEach(function (nodeName) {
            var node = cc.find(nodeName);
            if (node) {
                var sprite = node.getComponent(cc.Sprite);
                if (sprite) {
                    sprite.spriteFrame = null;
                }
            }
            else {
                // 如果直接找不到，尝试在Canvas下查找
                var canvas = cc.find('Canvas');
                if (canvas) {
                    var foundNode = _this.findNodeByName(canvas, nodeName);
                    if (foundNode) {
                        var sprite = foundNode.getComponent(cc.Sprite);
                        if (sprite) {
                            sprite.spriteFrame = null;
                        }
                    }
                }
            }
        });
    };
    /**
     * 递归查找节点
     */
    GamePageController.prototype.findNodeByName = function (parent, name) {
        if (parent.name === name) {
            return parent;
        }
        for (var _i = 0, _a = parent.children; _i < _a.length; _i++) {
            var child = _a[_i];
            var found = this.findNodeByName(child, name);
            if (found) {
                return found;
            }
        }
        return null;
    };
    /**
     * 清理视觉元素（退出游戏和结算时调用）
     */
    GamePageController.prototype.cleanupVisualElements = function () {
        // 只恢复方块初始状态（清理动画状态），不清理动物预制体
        // 动物预制体（包括头像）应该保持显示，只在游戏重新开始时重新生成
        if (this.blockAnimationManager) {
            this.blockAnimationManager.stopAllAnimations();
            // 刷新方块引用，确保引用是最新的
            this.blockAnimationManager.refreshBlockReferences();
        }
        // 清理倒计时状态（退出游戏时隐藏倒计时，并移除动画完成回调）
        this.isCountdownActive = false;
        this.isWaitingPhase = false;
        this.lastCountdownEventId = "";
        if (this.countdownNode) {
            var sk = this.countdownNode.getComponent(sp.Skeleton);
            if (sk) {
                sk.setCompleteListener(null);
            }
            this.countdownNode.active = false;
        }
        // 清理视觉元素时也隐藏回合数显示
        this.hideRoundDisplay();
        // 重置回合数显示
        this.hideRoundDisplay();
        this.currentRound = 1;
    };
    /**
     * 完整的游戏状态重置（GameStart时调用）
     */
    GamePageController.prototype.resetGameState = function () {
        // 1. 重置掉落检测状态
        this.resetFallDetectionState();
        // 2. 重置帧同步相关状态
        this.currentLogicFrame = 0;
        this.clientFrameId = 0; // 重置客户端帧ID
        this.lastBatchSendTime = Date.now();
        this.frameInputBuffer = [];
        this.isFrameInputStopped = false; // 重要：重置输入发送状态，允许第二把游戏发送输入
        // 重置摇杆相关状态
        this.isPlayerMoving = false;
        this.currentPlayerVelocity = cc.Vec2.ZERO;
        // 不在此处更改摇杆可交互状态，避免覆盖等待阶段(事件1)已设置的禁用
        // 2.1 清空上局的权威坐标/和解相关状态，避免把本地拉回上局死亡点
        this.lastServerPosition = null;
        this.isCollisionFollowing = false;
        this.isLocalFrozen = false;
        this.frozenLocalPos = cc.v2(0, 0);
        this.consecutiveCollisionFrames = 0;
        this.reconciliationHistory = [];
        this.lastReconciliationTime = 0;
        // 清理弱网缓冲/目标位置，避免使用上局的平滑缓冲
        this.playerPositionBuffers.clear && this.playerPositionBuffers.clear();
        this.targetPositions.clear();
        this.playerMovingStates.clear();
        // 3. 清理逻辑和渲染状态
        this.logicStates.clear();
        this.renderStates.clear();
        // 清理平滑移动系统状态
        this.reconciliationStates.clear();
        // 4. 重置网络抖动统计和网络质量评估
        this.networkJitterHistory = [];
        this.adaptiveRenderDelay = this.renderDelay;
        // 🎯 重置网络质量评估相关指标，确保新游戏从良好状态开始
        this.networkQuality = 'good';
        this.packetLossRate = 0;
        this.averageLatency = 0;
        this.latencyHistory = [];
        this.lastFrameReceiveTime = 0;
        this.totalPackets = 0;
        this.jitterSum = 0;
        this.jitterCount = 0;
        this.packetWindow = [];
        // 5. 重置所有方块到显示状态并清空动画（避免上一把隐藏状态残留）
        if (!this.blockAnimationManager) {
            this.initializeBlockAnimationManager();
        }
        if (this.blockAnimationManager) {
            this.blockAnimationManager.stopAllAnimations();
            this.blockAnimationManager.refreshBlockReferences();
        }
        else {
            console.warn('方块动画管理器未初始化，无法在重置时恢复方块可见性');
        }
        // 6. 彻底重置倒计时骨骼动画
        this.resetCountdownSkeletonAnimation();
        // 7. 重置jingbao系统状态
        this.resetJingbaoSystem();
        // 8. 重置GameOver显示
        this.hideGameOver();
        // 9. 重置回合数显示（避免上一把的回合数残留到新一把）
        this.hideRoundDisplay();
        this.currentRound = 1;
    };
    /**
     * 初始化坐标显示功能
     */
    GamePageController.prototype.initializeCoordinateDisplay = function () {
        if (!this.coordinateLabel) {
            console.warn("坐标显示标签未设置，跳过坐标显示功能初始化");
            this.isCoordinateDisplayEnabled = false;
            return;
        }
        // 设置标签样式
        this.coordinateLabel.string = "坐标: (0, 0)";
        this.coordinateLabel.fontSize = 24;
        this.coordinateLabel.node.color = cc.Color.WHITE;
        // 设置标签位置（左上角）
        var canvas = cc.find("Canvas");
        if (canvas) {
            var canvasSize = canvas.getContentSize();
            this.coordinateLabel.node.setPosition(-canvasSize.width / 2 + 120, // 距离左边120像素
            canvasSize.height / 2 - 50 // 距离顶部50像素
            );
        }
    };
    /**
   * 立即停止帧输入的采集和发送（断线时调用）
   * 同时隐藏摇杆，防止玩家在断线期间继续操作
   */
    GamePageController.prototype.stopFrameInputImmediately = function () {
        this.isFrameInputStopped = true;
        this.frameInputBuffer = [];
        // 隐藏并禁用摇杆
        if (this.joystickController) {
            this.joystickController.setInteractable(false);
            console.log('[GamePageController] 断线检测，摇杆已禁用');
        }
        console.log('[GamePageController] 断线检测，立即停止帧输入发送');
    };
    // ========== 断线重连相关方法 ==========
    /**
     * 处理断线重连逻辑
     * 当EnterRoom响应中的roomId > 0时，表示这是断线重连
     * @param enterRoomData EnterRoom响应数据
     */
    GamePageController.prototype.handleReconnection = function (enterRoomData) {
        var _this = this;
        // 1. 判断是否为断线重连
        if (!this.isReconnection(enterRoomData)) {
            return;
        }
        // 标记重连流程中，避免不必要的启用操作（保留标识，便于后续扩展）
        this.isReconnecting = true;
        // 1.1 在恢复阶段之前，先根据 EnterRoom 的 users 预标记死亡玩家，
        // 确保后续 resume* 阶段中的 isLocalPlayerDead() 判断立刻正确，防止摇杆被短暂启用
        try {
            if (enterRoomData && Array.isArray(enterRoomData.users)) {
                this.premarkFallenFromEnterRoom(enterRoomData.users);
            }
        }
        catch (e) {
            console.warn('[Reconnection] 预标记死亡玩家失败:', e);
        }
        // 2. 清理当前场景
        this.cleanupSceneForReconnection();
        // 2.1 初始化本地客户端帧号，使断线重连后的 FrameInput 帧号从后端给定数字开始
        this.initializeClientFrameFromReconnection(enterRoomData);
        // 3. 根据消息重新生成场景元素
        this.regenerateSceneFromReconnectionData(enterRoomData);
        // 4. 根据gameStatus判断游戏阶段并执行相应逻辑
        this.resumeGameStateFromReconnection(enterRoomData);
        // 5. 最后的最后：确保玩家状态显示正确（延迟执行避免被其他逻辑覆盖）
        this.scheduleOnce(function () {
            _this.handleReconnectionUserData(enterRoomData.users);
            // 在 EnterRoom 重连流程完成后，才恢复帧输入的采集与发送
            _this.frameInputBuffer = []; // 清空残留缓冲
            _this.lastBatchSendTime = Date.now(); // 对齐计时起点
            _this.isFrameInputStopped = false; // 解除停止，允许继续发送 FrameInput
            // 重连流程结束
            _this.isReconnecting = false;
        }, 0.1);
    };
    /**
     * 判断是否为断线重连
     * @param enterRoomData EnterRoom响应数据
     * @returns 是否为断线重连
     */
    GamePageController.prototype.isReconnection = function (enterRoomData) {
        // 根据API文档：roomId > 0 表示断线重连
        return enterRoomData && enterRoomData.roomId && enterRoomData.roomId > 0;
    };
    /**
     * 断线重连时清理场景
     * 清理背景、玩家头像预制体和动物预制体
     */
    GamePageController.prototype.cleanupSceneForReconnection = function () {
        // 1. 清理所有动物预制体
        this.clearAllAnimals();
        // 2. 清理头像和分数界面
        if (this.gameLayoutController) {
            this.gameLayoutController.clearScoreView();
        }
        // 3. 重置游戏状态（但不完全重置，保留一些重连需要的状态）
        this.resetGameStateForReconnection();
        // 4. 清理方块动画状态
        if (this.blockAnimationManager) {
            this.blockAnimationManager.stopAllAnimations();
            this.blockAnimationManager.refreshBlockReferences();
        }
    };
    /**
     * 彻底重置倒计时骨骼动画状态
     */
    GamePageController.prototype.resetCountdownSkeletonAnimation = function () {
        if (this.countdownNode) {
            var skeletonComponent = this.countdownNode.getComponent(sp.Skeleton);
            if (skeletonComponent) {
                try {
                    // 1. 停止当前所有正在播放的动画
                    skeletonComponent.clearTrack(0); // 清除轨道0的动画
                    // 2. 清除所有动画轨道，彻底移除已设置的动画
                    skeletonComponent.clearTracks();
                    // 3. 将骨骼恢复到初始姿势（绑定姿势），解决残留帧问题
                    skeletonComponent.setToSetupPose();
                    // 4. 清除完成监听器
                    skeletonComponent.setCompleteListener(null);
                }
                catch (error) {
                    console.error("❌ 重置倒计时骨骼动画失败:", error);
                }
            }
            // 隐藏节点
            this.countdownNode.active = false;
        }
    };
    /**
     * 为断线重连重置游戏状态（部分重置）
     */
    GamePageController.prototype.resetGameStateForReconnection = function () {
        // 重置掉落检测相关状态：
        // 注意：不要在这里清空 fallenPlayers，避免在断线重连恢复阶段把“已死亡”的状态短暂清空，
        // 导致 resume* 阶段误判为存活从而启用摇杆，出现“显示→隐藏”的闪烁。
        // fallenPlayers 将在 handleReconnection() 中根据 EnterRoom.users 进行预标记与更新。
        // 保留已有的死亡集合，fallenBlocks 可以清空。
        this.fallenBlocks.clear();
        // 重置帧同步相关状态
        this.frameHistory.clear();
        this.frameInputBuffer = [];
        // 重连初期先停止发送，待初始化完成并收到权威帧后再恢复
        this.isFrameInputStopped = true;
        this.isReconnecting = true;
        // 重置摇杆状态
        this.isPlayerMoving = false;
        this.currentPlayerVelocity = cc.Vec2.ZERO;
        // 重置逻辑和渲染状态
        this.logicStates.clear();
        this.renderStates.clear();
        this.targetPositions.clear();
        this.playerMovingStates.clear();
        // 重置倒计时状态（不使用计时器，改为依赖骨骼动画完成事件）
        this.isCountdownActive = false;
        this.isWaitingPhase = false;
        this.lastCountdownEventId = "";
        // 彻底重置倒计时骨骼动画
        this.resetCountdownSkeletonAnimation();
        // 重置回合数显示
        this.hideRoundDisplay();
        this.currentRound = 1;
        // 重置输入阻塞状态
        this.isInputBlocked = false;
        this.isLocalFrozen = false;
        // 隐藏GameOver节点（重连时不应显示）
        this.hideGameOver();
        // 重置jingbao系统状态
        this.resetJingbaoSystem();
    };
    /**
     * 显示GameOver节点
     */
    GamePageController.prototype.showGameOver = function () {
        if (this.gameoverNode) {
            this.gameoverNode.active = true;
        }
        else {
            console.warn("gameoverNode 未设置，无法显示GameOver");
        }
    };
    /**
     * 隐藏GameOver节点
     */
    GamePageController.prototype.hideGameOver = function () {
        if (this.gameoverNode) {
            this.gameoverNode.active = false;
        }
    };
    /**
     * 根据断线重连数据重新生成场景元素
     * @param enterRoomData EnterRoom响应数据
     */
    GamePageController.prototype.regenerateSceneFromReconnectionData = function (enterRoomData) {
        // 1. 重新生成背景（根据mapType）
        this.regenerateBackground(enterRoomData.mapType);
        // 2. 重新生成头像和分数界面
        this.regenerateScoreView(enterRoomData.users);
        // 3. 重新生成动物预制体（按后端位置）
        this.regeneratePlayerAnimals(enterRoomData.users);
        // 4. 设置方块皮肤（在方块动画管理器初始化之后）
        this.setBlockSkinFromMapType();
        // 5. 场景重建完成（状态恢复将在handleReconnection方法最后延迟执行）
    };
    /**
     * 重新生成背景
     * @param mapType 地图类型（1-火山，2-冰川）
     */
    GamePageController.prototype.regenerateBackground = function (mapType) {
        // 通过全局单例设置背景图片，避免依赖节点名称
        var gm = GlobalManagerController_1.getGlobalManager();
        if (gm) {
            gm.setMapBackground(mapType);
        }
        else {
            console.warn('❌ 未获取到GlobalManagerController实例，尝试临时查找Canvas内组件');
            var canvas = cc.find('Canvas');
            if (canvas) {
                var comp = canvas.getComponentInChildren('GlobalManagerController');
                if (comp && typeof comp.setMapBackground === 'function') {
                    comp.setMapBackground(mapType);
                }
                else {
                    console.error('❌ 仍未找到GlobalManagerController或setMapBackground方法');
                }
            }
            else {
                console.error('❌ 未找到Canvas，无法设置背景');
            }
        }
    };
    /**
     * 重新生成头像和分数界面
     */
    GamePageController.prototype.regenerateScoreView = function (users) {
        if (this.gameLayoutController) {
            // 将users数据转换为PlayerData格式
            var playerDataArray = users.map(function (user) { return ({
                userId: user.userId,
                playerNumber: user.playerNumber || 1,
                x: user.x || 0,
                y: user.y || 0,
                playerType: user.playerType || 'red',
                surviveTime: 0,
                rank: 0,
                nickName: user.nickName,
                avatar: user.avatar,
                coin: user.coin,
                bpStatus: user.bpStatus,
                pos: user.pos,
                status: user.status,
                score: user.score
            }); });
            // 临时设置gameStartData用于创建分数界面
            var tempGameStartData = {
                roomId: 0,
                roomType: 1,
                playerNum: playerDataArray.length,
                mapType: 1,
                fee: 0,
                users: playerDataArray,
                gameStatus: 1,
                countDown: 0,
                propMode: 0,
                specialFull: 0,
                specialRemoved: 0,
                blockList: [],
                opIndex: 0,
                lookPos: 0,
                curTask: undefined
            };
            var originalGameStartData = GlobalBean_1.GlobalBean.GetInstance().noticeStartGame;
            GlobalBean_1.GlobalBean.GetInstance().noticeStartGame = tempGameStartData;
            // 创建分数界面
            this.gameLayoutController.createScoreView();
            // 恢复原始数据
            GlobalBean_1.GlobalBean.GetInstance().noticeStartGame = originalGameStartData;
        }
    };
    /**
     * 重新生成动物预制体（按后端返回的位置）
     * @param users 玩家数据数组
     */
    GamePageController.prototype.regeneratePlayerAnimals = function (users) {
        var _a, _b;
        var myUserId = (_b = (_a = GlobalBean_1.GlobalBean.GetInstance().loginData) === null || _a === void 0 ? void 0 : _a.userInfo) === null || _b === void 0 ? void 0 : _b.userId;
        if (!myUserId) {
            console.error("无法获取玩家ID，无法重新生成动物预制体");
            return;
        }
        var _loop_2 = function (user) {
            // 🎯 计算保底位置：当坐标无效或落在默认中心(0,0)时，按座位号落到四角
            var isValidNum = function (v) { return typeof v === 'number' && !Number.isNaN(v) && Number.isFinite(v); };
            var spawnX = isValidNum(user.x) ? user.x : Number.NaN;
            var spawnY = isValidNum(user.y) ? user.y : Number.NaN;
            var isCenter = function (vx, vy) { return isValidNum(vx) && isValidNum(vy) && Math.abs(vx) < 1e-3 && Math.abs(vy) < 1e-3; };
            if (!isValidNum(spawnX) || !isValidNum(spawnY) || isCenter(spawnX, spawnY)) {
                // 使用服务端规范的默认出生点（单位：像素）
                var playerNumber = user.playerNumber || 1;
                switch (playerNumber) {
                    case 1: // 左上
                        spawnX = -240;
                        spawnY = 258;
                        break;
                    case 2: // 右上
                        spawnX = 240;
                        spawnY = 258;
                        break;
                    case 3: // 左下
                        spawnX = -240;
                        spawnY = -225;
                        break;
                    default: // 4或其他 -> 右下
                        spawnX = 240;
                        spawnY = -255;
                        break;
                }
                cc.warn("[Reconnect Spawn] \u65E0\u6548\u6216\u7F3A\u5931\u5750\u6807\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u51FA\u751F\u70B9 userId=" + user.userId + ", playerNumber=" + playerNumber + ", fallback=(" + spawnX.toFixed(1) + ", " + spawnY.toFixed(1) + ")", { x: user.x, y: user.y });
            }
            else {
                console.log("[Reconnect Spawn] \u4F7F\u7528\u670D\u52A1\u5668\u5750\u6807 userId=" + user.userId + ", playerNumber=" + (user.playerNumber || 1) + ", position=(" + spawnX.toFixed(1) + ", " + spawnY.toFixed(1) + ")");
            }
            // 存储玩家数据（使用计算后的保底位置）
            var playerData = {
                userId: user.userId,
                playerNumber: user.playerNumber || 1,
                x: spawnX,
                y: spawnY,
                playerType: user.playerType || 'red',
                surviveTime: 0,
                rank: 0,
                nickName: user.nickName,
                avatar: user.avatar,
                coin: user.coin,
                bpStatus: user.bpStatus,
                pos: 0,
                status: 0,
                score: 0
            };
            this_2.allPlayerData.set(user.userId, playerData);
            // 只为存活的玩家生成动物预制体
            if (user.bpStatus === 1) { // 1-存活，2-死亡
                // 生成动物预制体（使用计算后的保底位置）
                var animalNode = this_2.createAnimalPrefab(user.playerType, spawnX, spawnY, user.userId, user.playerNumber);
                if (animalNode) {
                    // 存储动物节点
                    this_2.allPlayerAnimals.set(user.userId, animalNode);
                    // 如果是自己的动物，特别标记
                    if (user.userId === myUserId) {
                        this_2.myPlayerData = playerData;
                        this_2.myAnimalNode = animalNode;
                    }
                    // 🔍 调试：验证动物节点创建后的实际位置
                    console.log("[regeneratePlayerAnimals] \u52A8\u7269\u8282\u70B9\u521B\u5EFA\u5B8C\u6210 userId=" + user.userId + ", \u5B9E\u9645\u4F4D\u7F6E=(" + animalNode.x.toFixed(1) + ", " + animalNode.y.toFixed(1) + ")");
                }
                else {
                    console.error("\u274C \u4E3A\u73A9\u5BB6 " + user.userId + " \u751F\u6210\u52A8\u7269\u9884\u5236\u4F53\u5931\u8D25");
                }
            }
            else {
                // 将已死亡的玩家添加到掉落列表中
                this_2.fallenPlayers.add(user.userId);
            }
        };
        var this_2 = this;
        // 为每个玩家重新生成动物预制体
        for (var _i = 0, users_2 = users; _i < users_2.length; _i++) {
            var user = users_2[_i];
            _loop_2(user);
        }
    };
    /**
     * 根据gameStatus恢复游戏状态并执行相应逻辑
     * @param enterRoomData EnterRoom响应数据
     */
    GamePageController.prototype.resumeGameStateFromReconnection = function (enterRoomData) {
        var gameStatus = enterRoomData.gameStatus;
        var countDown = enterRoomData.countDown || 0;
        var currentBlocks = enterRoomData.currentBlocks;
        var currentRound = enterRoomData.currentRound || 1;
        // 更新当前游戏阶段
        this.currentGamePhase = gameStatus;
        // 根据不同的游戏状态执行相应逻辑
        switch (gameStatus) {
            case 1: // 等待阶段 - 游戏开始倒计时
                this.resumeWaitingPhase(countDown, currentRound);
                break;
            case 2: // 回合等待 - 补齐后等待下一回合
                this.resumeRoundWaitingPhase(countDown, currentRound);
                break;
            case 3: // 松动阶段 - 方块开始松动
                this.resumeShakingPhase(countDown, currentBlocks, currentRound);
                break;
            case 4: // 掉落阶段 - 方块开始掉落
                this.resumeFallingPhase(countDown, currentBlocks, currentRound);
                break;
            case 5: // 补齐阶段 - 方块补齐
                this.resumeRestoringPhase(countDown, currentBlocks, currentRound);
                break;
            case 6: // 结束阶段 - 游戏结束
                this.resumeEndingPhase();
                break;
            default:
                console.warn("\u672A\u77E5\u7684\u6E38\u620F\u72B6\u6001: " + gameStatus);
                break;
        }
        // 恢复帧同步状态
        if (enterRoomData.currentFrameId) {
            this.confirmedFrameId = enterRoomData.currentFrameId;
        }
    };
    /**
     * 恢复等待阶段
     */
    GamePageController.prototype.resumeWaitingPhase = function (countDown, currentRound) {
        this.isWaitingPhase = true;
        this.isLocalFrozen = true;
        // 🎯 修复断线重连位置被拉回(0,0)的问题：更新冻结位置为当前玩家位置
        if (this.myAnimalNode && cc.isValid(this.myAnimalNode)) {
            this.frozenLocalPos = cc.v2(this.myAnimalNode.x, this.myAnimalNode.y);
            console.log("[resumeWaitingPhase] \u66F4\u65B0\u51BB\u7ED3\u4F4D\u7F6E: (" + this.frozenLocalPos.x.toFixed(1) + ", " + this.frozenLocalPos.y.toFixed(1) + ")");
        }
        else {
            this.frozenLocalPos = cc.v2(0, 0);
            console.warn("[resumeWaitingPhase] \u672C\u5730\u73A9\u5BB6\u8282\u70B9\u4E0D\u5B58\u5728\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u51BB\u7ED3\u4F4D\u7F6E(0,0)");
        }
        // 禁用摇杆交互
        if (this.joystickController) {
            this.joystickController.setInteractable(false);
        }
        // 断线重连时不播放倒计时动画，只设置状态
        // 断线重连时更新回合数显示（如果当前回合数大于0）
        if (currentRound > 0) {
            this.updateRoundDisplay(currentRound);
        }
    };
    /**
     * 恢复回合等待阶段
     */
    GamePageController.prototype.resumeRoundWaitingPhase = function (countDown, currentRound) {
        this.isWaitingPhase = true;
        // 启用摇杆交互（回合等待期间玩家可以移动）
        // 但如果本地玩家已死亡，则保持摇杆禁用状态
        if (this.joystickController && !this.isLocalPlayerDead()) {
            this.joystickController.setInteractable(true);
            // 摇杆启用时隐藏提示动画
            this.hideTishiAnimation();
        }
        // 断线重连时不播放倒计时动画，只设置状态
        // 断线重连时更新回合数显示
        if (currentRound > 0) {
            this.updateRoundDisplay(currentRound);
        }
    };
    /**
     * 恢复松动阶段
     */
    GamePageController.prototype.resumeShakingPhase = function (countDown, currentBlocks, currentRound) {
        // 启用摇杆交互
        // 但如果本地玩家已死亡，则保持摇杆禁用状态
        if (this.joystickController && !this.isLocalPlayerDead()) {
            this.joystickController.setInteractable(true);
            // 摇杆启用时隐藏提示动画
            this.hideTishiAnimation();
        }
        // 断线重连时：拿到方块数据，直接播放方块抖动动画直到游戏状态切换
        if (currentBlocks && currentBlocks.length > 0) {
            this.resumeBlockShakeAnimation(currentBlocks);
            // 播放方块音效
            this.playBlockSoundEffect(3); // eventType: 3 - 松动阶段
        }
        // 断线重连时更新回合数显示
        if (currentRound > 0) {
            this.updateRoundDisplay(currentRound);
        }
    };
    /**
     * 恢复掉落阶段
     */
    GamePageController.prototype.resumeFallingPhase = function (countDown, currentBlocks, currentRound) {
        // 启用摇杆交互
        // 但如果本地玩家已死亡，则保持摇杆禁用状态
        if (this.joystickController && !this.isLocalPlayerDead()) {
            this.joystickController.setInteractable(true);
            // 摇杆启用时隐藏提示动画
            this.hideTishiAnimation();
        }
        // 断线重连时：gameStatus==4时，直接隐藏格子，不播放动画
        if (currentBlocks && currentBlocks.length > 0) {
            this.updateFallenBlocks(currentBlocks);
            this.hideBlocksDirectly(currentBlocks);
            // 播放方块音效
            this.playBlockSoundEffect(4); // eventType: 4 - 掉落阶段
        }
        // 断线重连时更新回合数显示
        if (currentRound > 0) {
            this.updateRoundDisplay(currentRound);
        }
    };
    /**
     * 恢复补齐阶段
     */
    GamePageController.prototype.resumeRestoringPhase = function (countDown, currentBlocks, currentRound) {
        // 启用摇杆交互
        // 但如果本地玩家已死亡，则保持摇杆禁用状态
        if (this.joystickController && !this.isLocalPlayerDead()) {
            this.joystickController.setInteractable(true);
            // 摇杆启用时隐藏提示动画
            this.hideTishiAnimation();
        }
        // 断线重连时：gameStatus==5时，显示格子，不做任何动画
        if (currentBlocks && currentBlocks.length > 0) {
            this.showBlocksDirectly(currentBlocks);
            // 播放方块音效
            this.playBlockSoundEffect(5); // eventType: 5 - 补齐阶段
        }
        // 断线重连时更新回合数显示
        if (currentRound > 0) {
            this.updateRoundDisplay(currentRound);
        }
    };
    /**
     * 恢复结束阶段
     */
    GamePageController.prototype.resumeEndingPhase = function () {
        // 禁用摇杆交互
        if (this.joystickController) {
            this.joystickController.setInteractable(false);
        }
        // 停止帧输入发送
        this.isFrameInputStopped = true;
        this.isInputBlocked = true;
        // 游戏结束阶段隐藏回合数显示，防止带入下一局
        this.hideRoundDisplay();
    };
    /**
     * 恢复方块动画状态
     * @param blocks 方块数据
     * @param animationType 动画类型
     */
    GamePageController.prototype.resumeBlockAnimation = function (blocks, animationType) {
        var _this = this;
        if (!this.blockAnimationManager || !blocks || blocks.length === 0) {
            return;
        }
        // 将方块数据转换为BrickPartyBlock格式
        var brickPartyBlocks = blocks.map(function (block, index) { return ({
            id: index + 1,
            x: block.x || 0,
            y: block.y || 0,
            blockType: 1,
            animationState: _this.getAnimationStateFromType(animationType),
            animationDelay: 0
        }); });
        // 创建游戏事件数据
        var gameEventData = {
            eventType: this.getEventTypeFromAnimationType(animationType),
            currentRound: 1,
            countDown: 0,
            randomSeed: Math.floor(Math.random() * 100000),
            message: "",
            blocks: brickPartyBlocks
        };
        // 处理方块动画
        this.blockAnimationManager.handleGameEvent(gameEventData);
    };
    /**
     * 断线重连时恢复方块抖动动画（直接播放抖动动画直到游戏状态切换）
     * @param blocks 方块数据
     */
    GamePageController.prototype.resumeBlockShakeAnimation = function (blocks) {
        if (!this.blockAnimationManager || !blocks || blocks.length === 0) {
            return;
        }
        // 将方块数据转换为BrickPartyBlock格式
        var brickPartyBlocks = blocks.map(function (block, index) { return ({
            id: index + 1,
            x: block.x || 0,
            y: block.y || 0,
            blockType: 1,
            animationState: GameBean_1.BlockAnimationState.SHAKE,
            animationDelay: 0
        }); });
        // 创建游戏事件数据
        var gameEventData = {
            eventType: 3,
            currentRound: 1,
            countDown: 0,
            randomSeed: Math.floor(Math.random() * 100000),
            message: "",
            blocks: brickPartyBlocks
        };
        // 直接处理方块抖动动画，持续播放直到游戏状态切换
        this.blockAnimationManager.handleGameEvent(gameEventData);
    };
    /**
     * 断线重连时直接隐藏方块（不播放动画）
     * @param blocks 方块数据
     */
    GamePageController.prototype.hideBlocksDirectly = function (blocks) {
        if (!this.blockAnimationManager || !blocks || blocks.length === 0) {
            return;
        }
        // 直接隐藏指定的方块，不播放动画
        for (var _i = 0, blocks_2 = blocks; _i < blocks_2.length; _i++) {
            var block = blocks_2[_i];
            var gridX = block.x || 0;
            var gridY = block.y || 0;
            // 通过方块动画管理器直接隐藏方块
            this.blockAnimationManager.hideBlockDirectly(gridX, gridY);
        }
    };
    /**
     * 断线重连时直接显示方块（不播放动画）
     * @param blocks 方块数据
     */
    GamePageController.prototype.showBlocksDirectly = function (blocks) {
        if (!this.blockAnimationManager || !blocks || blocks.length === 0) {
            return;
        }
        // 直接显示指定的方块，不播放动画
        for (var _i = 0, blocks_3 = blocks; _i < blocks_3.length; _i++) {
            var block = blocks_3[_i];
            var gridX = block.x || 0;
            var gridY = block.y || 0;
            // 通过方块动画管理器直接显示方块
            this.blockAnimationManager.showBlockDirectly(gridX, gridY);
        }
    };
    /**
     * 根据动画类型获取动画状态
     */
    GamePageController.prototype.getAnimationStateFromType = function (animationType) {
        switch (animationType) {
            case 'shake':
                return GameBean_1.BlockAnimationState.SHAKE;
            case 'fall':
                return GameBean_1.BlockAnimationState.FALL;
            case 'rise':
                return GameBean_1.BlockAnimationState.RISE;
            default:
                return GameBean_1.BlockAnimationState.NONE;
        }
    };
    /**
     * 根据动画类型获取事件类型
     */
    GamePageController.prototype.getEventTypeFromAnimationType = function (animationType) {
        switch (animationType) {
            case 'shake':
                return 3; // 松动阶段
            case 'fall':
                return 4; // 掉落阶段
            case 'rise':
                return 5; // 补齐阶段
            default:
                return 1; // 等待阶段
        }
    };
    /**
     * 开始倒计时显示
     * @param countDown 倒计时时间（毫秒）
     */
    GamePageController.prototype.startCountdownDisplay = function (_countDown) {
        // 统一为“收到消息时播放完整的321go动画，动画完成后自动隐藏”
        this.playCountdownAnimation();
    };
    /**
     * 播放方块音效
     * @param eventType 事件类型 (3-块晃动, 4-块下落, 5-块上升)
     */
    GamePageController.prototype.playBlockSoundEffect = function (eventType) {
        try {
            var soundName = "";
            switch (eventType) {
                case 3: // 松动阶段 - 块晃动
                    // 根据当前回合数选择不同时长的晃动音效
                    soundName = this.getShakeSoundByRound(this.currentRound);
                    break;
                case 4: // 掉落阶段 - 块下落
                    soundName = "block_down"; // 使用现有的块下落音效
                    break;
                case 5: // 补齐阶段 - 块上升
                    soundName = "block_up"; // 使用现有的块上升音效
                    break;
                default:
                    console.warn("\u672A\u77E5\u7684\u65B9\u5757\u4E8B\u4EF6\u7C7B\u578B: " + eventType);
                    return;
            }
            // 使用AudioManager播放音效，支持音效开关控制
            AudioManager_1.AudioManager.playSound(soundName);
        }
        catch (error) {
            console.error("播放方块音效失败:", error);
        }
    };
    /**
     * 根据回合区间返回对应的方块晃动音效名
     * - 1-10关: 2s 震动音效 -> block_move_2s
     * - 11-20关: 1.5s 震动音效 -> block_move_1_5s
     * - 21-30关: 1s 震动音效 -> block_move_1s
     * 超出范围时兜底为旧音效 "block_move"
     */
    GamePageController.prototype.getShakeSoundByRound = function (roundNumber) {
        var round = (typeof roundNumber === 'number' && roundNumber > 0) ? roundNumber : 1;
        if (round >= 1 && round <= 10)
            return "block_move2.0s";
        if (round >= 11 && round <= 20)
            return "block_move1.5s";
        if (round >= 21 && round <= 30)
            return "block_move1.0s";
        return "block_move";
    };
    /*
     * 播放321go音效
     */
    GamePageController.prototype.play321GoSoundEffect = function () {
        try {
            // 使用AudioManager播放音效，支持音效开关控制
            AudioManager_1.AudioManager.playSound("321go");
        }
        catch (error) {
            console.error("播放321go音效失败:", error);
        }
    };
    /**
     * 播放死亡音效
     */
    GamePageController.prototype.playDeathSoundEffect = function (animalNode) {
        try {
            // 默认音效
            var soundKey = "death";
            // 如果能定位到动物节点，尝试根据动物类型选择专属死亡音效
            if (animalNode) {
                var userId = this.getPlayerIdByAnimalNode(animalNode);
                if (userId) {
                    var pdata = this.allPlayerData.get(userId);
                    var pType = pdata && pdata.playerType;
                    // playerType 与动物映射：red→fox, yellow→kangraoo, green→parrot, pink→pig
                    if (typeof pType === "string") {
                        switch (pType) {
                            case "red":
                                soundKey = "death_fox";
                                break;
                            case "yellow":
                                // 命名遵循需求：death_kangraoo（注意拼写与资源一致）
                                soundKey = "death_kangraoo";
                                break;
                            case "green":
                                soundKey = "death_parrot";
                                break;
                            case "pink":
                                soundKey = "death_pig";
                                break;
                            default:
                                soundKey = "death";
                                break;
                        }
                    }
                }
            }
            // 使用AudioManager播放音效，支持音效开关控制
            AudioManager_1.AudioManager.playSound(soundKey);
        }
        catch (error) {
            console.error("播放死亡音效失败:", error);
        }
    };
    /**
     * 播放死亡特效
     * @param animalNode 动物节点
{{ ... }}
     */
    GamePageController.prototype.playDeathEffect = function (animalNode) {
        try {
            // 获取当前地图类型
            var mapType = this.getCurrentMapType();
            // 根据地图类型选择死亡特效动画
            var deathEffectName = void 0;
            if (mapType === 1) {
                // 地图1（火山）播放yanjiang特效
                deathEffectName = "yanjiang";
            }
            else if (mapType === 2) {
                // 地图2（冰川）播放shuihua特效
                deathEffectName = "shuihua";
            }
            else {
                // 默认使用shuihua特效
                deathEffectName = "shuihua";
                console.warn("\u672A\u77E5\u7684\u5730\u56FE\u7C7B\u578B: " + mapType + "\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u6B7B\u4EA1\u7279\u6548");
            }
            // 创建死亡特效节点
            this.createDeathEffectNode(animalNode, deathEffectName);
        }
        catch (error) {
            console.error("播放死亡特效失败:", error);
        }
    };
    /**
     * 获取当前地图类型
     * @returns 地图类型（1-火山，2-冰川）
     */
    GamePageController.prototype.getCurrentMapType = function () {
        try {
            // 从全局数据中获取地图类型
            var gameStartData = GlobalBean_1.GlobalBean.GetInstance().noticeStartGame;
            if (gameStartData && gameStartData.mapType) {
                return gameStartData.mapType;
            }
            // 如果获取不到，默认返回地图1（火山）
            console.warn("无法获取地图类型，使用默认地图1（火山）");
            return 1;
        }
        catch (error) {
            console.error("获取地图类型失败:", error);
            return 1; // 默认返回地图1
        }
    };
    /**
     * 创建死亡特效节点
     * @param animalNode 动物节点
     * @param effectName 特效名称
     */
    GamePageController.prototype.createDeathEffectNode = function (animalNode, effectName) {
        var _this = this;
        try {
            // 如果没有死亡特效预制体，直接返回
            if (!this.deathEffectPrefab) {
                console.warn("死亡特效预制体未设置，无法播放死亡特效");
                return;
            }
            // 实例化死亡特效节点
            var deathEffectNode_1 = cc.instantiate(this.deathEffectPrefab);
            if (!deathEffectNode_1) {
                console.error("创建死亡特效节点失败");
                return;
            }
            // 设置特效节点位置（跟随动物节点当前位置，即偏移后的位置）
            var currentPosition = animalNode.getPosition();
            deathEffectNode_1.setPosition(currentPosition);
            // 将特效节点添加到场景中
            animalNode.parent.addChild(deathEffectNode_1);
            // 获取玩家ID用于跟踪特效节点
            var userId_1 = this.getPlayerIdByAnimalNode(animalNode);
            if (userId_1) {
                this.deathEffectNodes.set(userId_1, deathEffectNode_1);
            }
            // 获取骨骼动画组件
            var skeleton = deathEffectNode_1.getComponent(sp.Skeleton);
            if (!skeleton) {
                console.error("死亡特效节点上没有骨骼动画组件");
                deathEffectNode_1.destroy();
                if (userId_1) {
                    this.deathEffectNodes.delete(userId_1);
                }
                return;
            }
            // 播放死亡特效动画
            skeleton.setAnimation(0, effectName, false);
            // 设置动画完成监听器，动画播放完成后销毁节点
            skeleton.setCompleteListener(function () {
                if (deathEffectNode_1 && cc.isValid(deathEffectNode_1)) {
                    deathEffectNode_1.destroy();
                }
                if (userId_1) {
                    _this.deathEffectNodes.delete(userId_1);
                }
            });
        }
        catch (error) {
            console.error("创建死亡特效节点失败:", error);
        }
    };
    /**
     * 清理所有死亡特效节点
     */
    GamePageController.prototype.clearDeathEffectNodes = function () {
        try {
            // 使用传统的forEach来避免迭代器兼容性问题
            this.deathEffectNodes.forEach(function (deathEffectNode, userId) {
                if (deathEffectNode && cc.isValid(deathEffectNode)) {
                    deathEffectNode.destroy();
                }
            });
            this.deathEffectNodes.clear();
        }
        catch (error) {
            console.error("清理死亡特效节点失败:", error);
        }
    };
    // ========== 难度升级展示相关方法 ==========
    /**
     * 处理GameEventShowcase事件（难度升级展示阶段）
     * @param eventData 游戏事件数据
     */
    GamePageController.prototype.handleShowcaseEvent = function (eventData) {
        this.showJingbaoAnimation();
    };
    /**
     * 显示jingbao节点并播放难度升级动画
     */
    GamePageController.prototype.showJingbaoAnimation = function () {
        if (!this.jingbaoNode) {
            console.warn("⚠️ jingbao节点未设置，无法播放难度升级动画");
            return;
        }
        // 显示节点
        this.jingbaoNode.active = true;
        this.jingbaoNode.zIndex = 10000; // 确保在最前层显示
        this.jingbaoNode.opacity = 255;
        // 播放警报音效（难度升级音效），与其他音效保持一致用法
        this.playWarningSoundEffect();
        // 获取骨骼动画组件
        var skeletonComponent = this.jingbaoNode.getComponent(sp.Skeleton);
        if (skeletonComponent) {
            try {
                // 播放难度升级动画
                skeletonComponent.setCompleteListener(null); // 清除旧的监听器
                skeletonComponent.setAnimation(0, "green", true); // 循环播放
            }
            catch (error) {
                console.error("❌ 播放难度升级动画失败:", error);
            }
        }
        else {
            console.warn("⚠️ jingbao节点未找到骨骼动画组件");
        }
    };
    /**
     * 隐藏jingbao节点并停止动画
     */
    GamePageController.prototype.hideJingbaoAnimation = function () {
        if (!this.jingbaoNode) {
            return;
        }
        // 获取骨骼动画组件并停止动画
        var skeletonComponent = this.jingbaoNode.getComponent(sp.Skeleton);
        if (skeletonComponent) {
            try {
                // 停止动画并清除监听器
                skeletonComponent.clearTracks();
                skeletonComponent.setCompleteListener(null);
                skeletonComponent.setToSetupPose();
            }
            catch (error) {
                console.error("❌ 停止难度升级动画失败:", error);
            }
        }
        // 隐藏节点
        this.jingbaoNode.active = false;
    };
    /**
     * 播放难度升级的"warning"音效
     */
    GamePageController.prototype.playWarningSoundEffect = function () {
        try {
            // 使用AudioManager播放音效，支持音效开关控制
            AudioManager_1.AudioManager.playSound("warning");
        }
        catch (error) {
            console.warn("播放warning音效失败:", error);
        }
    };
    /**
     * 初始化jingbao系统
     */
    GamePageController.prototype.initializeJingbaoSystem = function () {
        if (!this.jingbaoNode) {
            console.warn("jingbao节点未设置，跳过jingbao系统初始化");
            return;
        }
        // 初始状态下隐藏jingbao节点
        this.jingbaoNode.active = false;
    };
    /**
     * 重置jingbao系统状态
     */
    GamePageController.prototype.resetJingbaoSystem = function () {
        this.hideJingbaoAnimation();
    };
    var GamePageController_1;
    // 【关键常量】逻辑帧时间，与服务器保持一致，避免位置预测误差
    GamePageController.LOGIC_FRAME_DT = 0.01; // 10ms
    __decorate([
        property(cc.Node)
    ], GamePageController.prototype, "boardBtnBack", void 0);
    __decorate([
        property(cc.Node)
    ], GamePageController.prototype, "forceCloseBtn", void 0);
    __decorate([
        property(LeaveDialogController_1.default)
    ], GamePageController.prototype, "leaveDialogController", void 0);
    __decorate([
        property(CongratsDialogController_1.default)
    ], GamePageController.prototype, "congratsDialogController", void 0);
    __decorate([
        property(GameLayoutController_1.default)
    ], GamePageController.prototype, "gameLayoutController", void 0);
    __decorate([
        property(MenuLayoutController_1.default)
    ], GamePageController.prototype, "menuLayoutController", void 0);
    __decorate([
        property(InfoDialogController_1.default)
    ], GamePageController.prototype, "infoDialogController", void 0);
    __decorate([
        property(cc.Label)
    ], GamePageController.prototype, "coordinateLabel", void 0);
    __decorate([
        property(VirtualJoystickController_1.default)
    ], GamePageController.prototype, "joystickController", void 0);
    __decorate([
        property(cc.Node)
    ], GamePageController.prototype, "playerNode", void 0);
    __decorate([
        property(cc.Prefab)
    ], GamePageController.prototype, "animalPrefab", void 0);
    __decorate([
        property(cc.Node)
    ], GamePageController.prototype, "countdownNode", void 0);
    __decorate([
        property(cc.Node)
    ], GamePageController.prototype, "roundDisplayNode", void 0);
    __decorate([
        property(cc.Node)
    ], GamePageController.prototype, "jingbaoNode", void 0);
    __decorate([
        property(cc.Node)
    ], GamePageController.prototype, "gameoverNode", void 0);
    __decorate([
        property(cc.Prefab)
    ], GamePageController.prototype, "deathEffectPrefab", void 0);
    __decorate([
        property(cc.Label)
    ], GamePageController.prototype, "debugInfoLabel", void 0);
    __decorate([
        property(cc.Node)
    ], GamePageController.prototype, "net", void 0);
    __decorate([
        property(cc.Node)
    ], GamePageController.prototype, "Network", void 0);
    __decorate([
        property([GamePlayerController_1.default])
    ], GamePageController.prototype, "playerAvatarControllers", void 0);
    __decorate([
        property(cc.Node)
    ], GamePageController.prototype, "blockParent", void 0);
    __decorate([
        property(cc.Node)
    ], GamePageController.prototype, "particle", void 0);
    __decorate([
        property(cc.Node)
    ], GamePageController.prototype, "particle1", void 0);
    __decorate([
        property({
            displayName: "玩家移动速度",
            tooltip: "玩家移动的最大速度（像素/秒）"
        })
    ], GamePageController.prototype, "playerMoveSpeed", void 0);
    __decorate([
        property({
            displayName: "移动速度缩放",
            tooltip: "移动速度的缩放因子，用于微调移动速度"
        })
    ], GamePageController.prototype, "infoClick", void 0);
    __decorate([
        property({
            displayName: "旋转速度(度/秒)",
            tooltip: "动物朝向旋转速度，单位: 度/秒。可在不同机型上微调手感"
        })
    ], GamePageController.prototype, "rotationSmoothSpeed", void 0);
    GamePageController = GamePageController_1 = __decorate([
        ccclass
    ], GamePageController);
    return GamePageController;
}(cc.Component));
exports.default = GamePageController;

cc._RF.pop();